summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ob-spice.org520
1 files changed, 260 insertions, 260 deletions
diff --git a/ob-spice.org b/ob-spice.org
index 514c55b..69c3b0e 100644
--- a/ob-spice.org
+++ b/ob-spice.org
@@ -163,66 +163,66 @@ use batch mode
* Code
#+BEGIN_SRC emacs-lisp :tangle no
- (add-to-list 'load-path "~/.emacs.d/lisp/ob-spice")
+(add-to-list 'load-path "~/.emacs.d/lisp/ob-spice")
#+END_SRC
** vars
#+BEGIN_SRC emacs-lisp
- (defvar org-babel-spice-eoe-indicator ":org_babel_spice_eoe"
- "String to indicate that evaluation has completed.")
+(defvar org-babel-spice-eoe-indicator ":org_babel_spice_eoe"
+ "String to indicate that evaluation has completed.")
#+END_SRC
** Session handling
#+BEGIN_SRC emacs-lisp
- (defvar org-babel-spice-command "ngspice"
- "Name of command to use for executing ngspice.")
- (defun org-babel-spice-initiate-session (&optional session dir _params)
- "Initiate a ngspice session.
- Create comint buffer SESSION running ngspice starting in
- default-directory or DIR if specified."
- (let* ((sessionname (if (or (not session) (string= session "none"))
- "spice" session))
- (session (make-comint sessionname org-babel-spice-command)))
- (if (and dir (string-match "^/" dir))
- ;; absolute dir
- (comint-simple-send session (format "cd %s" dir))
- ;; relative dir
- (comint-simple-send session (format "cd %s%s" default-directory (or dir ""))))
- session
- ))
- (defun org-babel-prep-session:spice (session params)
- "Prepare SESSION according to header arguments in PARAMS."
- (let ((session (org-babel-spice-initiate-session session))
- (var-lines (org-babel-variable-assignments:spice params)))
- (org-babel-comint-in-buffer session
- (sit-for .5) (goto-char (point-max))
- (mapc (lambda (var)
- (insert var) (comint-send-input nil t)
- (org-babel-comint-wait-for-output session)
- (sit-for .1) (goto-char (point-max))) var-lines))
- session))
- (defun org-babel-load-session:spice (session body params)
- "Load BODY into SESSION."
- (save-window-excursion
- (let ((buffer (org-babel-prep-session:spice session params)))
- (with-current-buffer buffer
- (goto-char (process-mark (get-buffer-process (current-buffer))))
- (insert (org-babel-chomp body)))
- buffer)))
-
- ;; helper
-
- (defun org-babel-variable-assignments:spice (params)
- "Return a list of spice statements to set the variables in PARAMS."
- (mapcar
- (lambda (pair)
- (format "set %s=%s"
- (car pair)
- (org-babel-spice-var-to-spice (cdr pair))))
- (org-babel--get-vars params)))
- (defun org-babel-spice-var-to-spice (var)
- "Convert VAR into a spice variable."
- (if (listp var)
- (concat "( " (mapconcat #'org-babel-spice-var-to-spice var " ") " )")
- (format "%S" var)))
+(defvar org-babel-spice-command "ngspice"
+ "Name of command to use for executing ngspice.")
+(defun org-babel-spice-initiate-session (&optional session dir _params)
+ "Initiate a ngspice session.
+Create comint buffer SESSION running ngspice starting in
+default-directory or DIR if specified."
+ (let* ((sessionname (if (or (not session) (string= session "none"))
+ "spice" session))
+ (session (make-comint sessionname org-babel-spice-command)))
+ (if (and dir (string-match "^/" dir))
+ ;; absolute dir
+ (comint-simple-send session (format "cd %s" dir))
+ ;; relative dir
+ (comint-simple-send session (format "cd %s%s" default-directory (or dir ""))))
+ session
+ ))
+(defun org-babel-prep-session:spice (session params)
+ "Prepare SESSION according to header arguments in PARAMS."
+ (let ((session (org-babel-spice-initiate-session session))
+ (var-lines (org-babel-variable-assignments:spice params)))
+ (org-babel-comint-in-buffer session
+ (sit-for .5) (goto-char (point-max))
+ (mapc (lambda (var)
+ (insert var) (comint-send-input nil t)
+ (org-babel-comint-wait-for-output session)
+ (sit-for .1) (goto-char (point-max))) var-lines))
+ session))
+(defun org-babel-load-session:spice (session body params)
+ "Load BODY into SESSION."
+ (save-window-excursion
+ (let ((buffer (org-babel-prep-session:spice session params)))
+ (with-current-buffer buffer
+ (goto-char (process-mark (get-buffer-process (current-buffer))))
+ (insert (org-babel-chomp body)))
+ buffer)))
+
+;; helper
+
+(defun org-babel-variable-assignments:spice (params)
+ "Return a list of spice statements to set the variables in PARAMS."
+ (mapcar
+ (lambda (pair)
+ (format "set %s=%s"
+ (car pair)
+ (org-babel-spice-var-to-spice (cdr pair))))
+ (org-babel--get-vars params)))
+(defun org-babel-spice-var-to-spice (var)
+ "Convert VAR into a spice variable."
+ (if (listp var)
+ (concat "( " (mapconcat #'org-babel-spice-var-to-spice var " ") " )")
+ (format "%S" var)))
#+END_SRC
** NEXT Variable handling & expand body
Don't replace variable calls in body. Instead set them in the spice
@@ -232,185 +232,185 @@ session: ~set x=4~\\
To set lists/arrays: ~set x=( 1 2 3 4 )~. Whitespace is important!
Access with ~$x[0]~ or ~$x[2-len]~.
#+BEGIN_SRC emacs-lisp
- ;; (lambda (text) (setq body (concat text "\n" body)))
- (defun org-babel-spice-vector-search (body vars)
- "Replace first instance in BODY for all VARS."
- (mapc (lambda (pair)
- (if (string-match (format
- "\\$%s\\[\\([0-9]\\)\\]"
- (car pair))
- body)
- (let ((replacement (nth
- (string-to-number (match-string 1 body))
- (cadr pair))))
- (setq body(format "%s%s%s"
- (substring body 0 (match-beginning
- 0))
- replacement
- (substring body (match-end 0)))))))
- vars)
- body
- )
+;; (lambda (text) (setq body (concat text "\n" body)))
+(defun org-babel-spice-vector-search (body vars)
+ "Replace first instance in BODY for all VARS."
+ (mapc (lambda (pair)
+ (if (string-match (format
+ "\\$%s\\[\\([0-9]\\)\\]"
+ (car pair))
+ body)
+ (let ((replacement (nth
+ (string-to-number (match-string 1 body))
+ (cadr pair))))
+ (setq body(format "%s%s%s"
+ (substring body 0 (match-beginning
+ 0))
+ replacement
+ (substring body (match-end 0)))))))
+ vars)
+ body
+ )
#+END_SRC
#+BEGIN_SRC emacs-lisp
- (defun org-babel-spice-replace-vars (body vars)
- "Expand BODY according to VARS."
- (let ((old-body ""))
- ;; replace vector variables preceded by '$' and followed by the
- ;; index in square brackets starting at 0. Matches without
- ;; preceding or succeeding spaces.
- (while (not (string= old-body body))
- (setq old-body body)
- (setq body (org-babel-spice-vector-search body vars))
- )
- ;; replace any variable names preceded by '$' with the actual
- ;; value of the variable. Matches only with succeeding space or
- ;; end of line to prevent namespace limitations.
- (mapc (lambda (pair)
- (setq body (replace-regexp-in-string
- (format "\\$%s\\( \\)\\|\\$%s$" (car pair)
- (car pair))
- (format "%s\1" (cdr pair))
- body)))
- vars)
- body))
- (defun org-babel-expand-body:spice (body params)
- "Expand BODY according to PARAMS, return the expanded body."
- (let ((vars (org-babel--get-vars params))
- (prologue (cdr (assq :prologue params)))
- (epilogue (cdr (assq :epilogue params)))
- (file (cdr (assq :file params))))
- (setq body (org-babel-spice-replace-vars body vars))
- ;; TODO :file stuff ....
-
- ;; add prologue/epilogue
- (when prologue (setq body (concat prologue "\n" body)))
- (when epilogue (setq body (concat body "\n" epilogue)))
- body))
+(defun org-babel-spice-replace-vars (body vars)
+ "Expand BODY according to VARS."
+ (let ((old-body ""))
+ ;; replace vector variables preceded by '$' and followed by the
+ ;; index in square brackets starting at 0. Matches without
+ ;; preceding or succeeding spaces.
+ (while (not (string= old-body body))
+ (setq old-body body)
+ (setq body (org-babel-spice-vector-search body vars))
+ )
+ ;; replace any variable names preceded by '$' with the actual
+ ;; value of the variable. Matches only with succeeding space or
+ ;; end of line to prevent namespace limitations.
+ (mapc (lambda (pair)
+ (setq body (replace-regexp-in-string
+ (format "\\$%s\\( \\)\\|\\$%s$" (car pair)
+ (car pair))
+ (format "%s\1" (cdr pair))
+ body)))
+ vars)
+ body))
+(defun org-babel-expand-body:spice (body params)
+ "Expand BODY according to PARAMS, return the expanded body."
+ (let ((vars (org-babel--get-vars params))
+ (prologue (cdr (assq :prologue params)))
+ (epilogue (cdr (assq :epilogue params)))
+ (file (cdr (assq :file params))))
+ (setq body (org-babel-spice-replace-vars body vars))
+ ;; TODO :file stuff ....
+
+ ;; add prologue/epilogue
+ (when prologue (setq body (concat prologue "\n" body)))
+ (when epilogue (setq body (concat body "\n" epilogue)))
+ body))
#+END_SRC
** ob-execute
#+BEGIN_SRC emacs-lisp
- (defun org-babel-spice-trim-body (body)
- "Prepare BODY to be used in interactive ngspice session."
- ;; random control codes after $var inserts
+(defun org-babel-spice-trim-body (body)
+ "Prepare BODY to be used in interactive ngspice session."
+ ;; random control codes after $var inserts
+ (replace-regexp-in-string
+ "" " "
+ ;; .control .endc lines
+ (replace-regexp-in-string
+ "^ *\\.\\(control\\|endc\\) *$" ""
+ ;; comment lines
(replace-regexp-in-string
- "" " "
- ;; .control .endc lines
- (replace-regexp-in-string
- "^ *\\.\\(control\\|endc\\) *$" ""
- ;; comment lines
- (replace-regexp-in-string
- "^ *\\*.*$" "" body))))
- (defun org-babel-execute:spice (body params)
- "Execute a block of Spice code with Babel.
- This function is called by `org-babel-execute-src-block'."
- (let* (;(body (org-babel-expand-body:spice body params))
- (gnuplot (cdr (assq :gnuplot params)))
- (result-params (cdr (assq :result-params params)))
- (result-type (cdr (assq :result-type params)))
- (session (org-babel-spice-initiate-session
- (cdr (assq :session params))
- (cdr (assq :dir params))))
- (vars (org-babel--get-vars params))
- (no-source (cdr (assq :no-source params)))
- (break-index (if (string-match "^ *\.end *$" body)
- (match-end 0) 0))
- ;;vars need to be replaced as they don't work when using source
- (circuit-body (org-babel-expand-body:spice
- (substring body 0 break-index)
- (assq-delete-all :epilogue (copy-alist params))))
- ;; todo: replace vars. :-( → set vars break when doing something like $file.txt
- (control-body (org-babel-spice-trim-body (substring body break-index)))
- (full-control-body (if control-body
- (org-babel-expand-body:generic
- control-body
- (assq-delete-all :prologue (copy-alist params))
- (org-babel-variable-assignments:spice params))))
- (circuit-file (if circuit-body (org-babel-temp-file "spice-body-" ".cir")))
- (result))
-
-
- (message (concat "circuit:\n" circuit-body))
- (message (concat "\n-----\ncontrol:\n" control-body))
-
- ;; Source circuit-body
- (with-temp-file circuit-file (insert circuit-body))
- (org-babel-spice-source session circuit-file)
- ;; Run control-body
- (setq result (org-babel-spice-evaluate session full-control-body result-type result-params))
-
-
- ;; TODO deal with temporary files
-
- ;;(org-babel-eval "ngspice -b " body)
- ;; Write body to temp file & execute with ngspice comint buffer and ~source file~
-
-
- ;; TODO read outputs from files
-
- ;; TODO gnuplot options
- (if (string= "yes" gnuplot)
- nil ;return content(!) of gnuplot.plt for :post processing with gnuplot block?
- nil ;return normal spice output
- )
- result
- ))
- (defun org-babel-spice-source (buffer file)
- "Source FILE in ngspice process running in BUFFER and return results."
- (let ((body (concat "source " file)))
- (org-babel-spice-evaluate buffer body 'value)))
- (defun org-babel-spice-evaluate (buffer body result-type &optional result-params)
- "Use BUFFER running ngspice process to eval BODY and return results.
- If RESULT-TYPE equals `output' return all outputs, if it equals
- `value' return only value of last statement."
- (let ((eoe-string (format "echo \"%s\"" org-babel-spice-eoe-indicator)))
- (pcase result-type
- (`output
- ;; Force session to be ready
- ;;(org-babel-comint-with-output
- ;; (buffer org-babel-spice-eoe-indicator t eoe-string)
- ;; (insert eoe-string) (comint-send-input nil t))
- ;; Eval body
- (replace-regexp-in-string
- "^\\(ngspice [0-9]+ -> \\)*" ""
- (mapconcat
- #'identity
- (butlast
- (cdr
- (split-string
- (mapconcat
- #'org-trim
- (org-babel-comint-with-output (buffer org-babel-spice-eoe-indicator t body)
- (mapcar (lambda (line)
- (insert (org-babel-chomp line)) (comint-send-input nil t))
- (list body
- eoe-string
- "\n")))
- "\n") "[\r\n]")) 2) "\n"))
- )
- (`value
- (let ((tmp-file (org-babel-temp-file "spice-")))
- (org-babel-comint-with-output
- (buffer org-babel-spice-eoe-indicator t body)
- (mapcar
- (lambda (line)
- (insert (org-babel-chomp line)) (comint-send-input nil t))
- (append (list body)
- (list (format "!! > %s" tmp-file)
- (format "echo \"%s\"" org-babel-spice-eoe-indicator)
- )))
- (comint-send-input nil t))
- ;; split result to output multiple comma separated vars as table
- (let ((result (split-string (org-babel-chomp (org-babel-eval-read-file tmp-file)) ",")))
- (if (cdr result)
- result
- (car result))
- )))
- ;;todo: add "smart" result type to display measurements (or echos?) & plot filenames
- )))
-
- (provide 'ob-spice)
- ;;; ob-spice.el ends here
+ "^ *\\*.*$" "" body))))
+(defun org-babel-execute:spice (body params)
+ "Execute a block of Spice code with Babel.
+This function is called by `org-babel-execute-src-block'."
+ (let* (;(body (org-babel-expand-body:spice body params))
+ (gnuplot (cdr (assq :gnuplot params)))
+ (result-params (cdr (assq :result-params params)))
+ (result-type (cdr (assq :result-type params)))
+ (session (org-babel-spice-initiate-session
+ (cdr (assq :session params))
+ (cdr (assq :dir params))))
+ (vars (org-babel--get-vars params))
+ (no-source (cdr (assq :no-source params)))
+ (break-index (if (string-match "^ *\.end *$" body)
+ (match-end 0) 0))
+ ;;vars need to be replaced as they don't work when using source
+ (circuit-body (org-babel-expand-body:spice
+ (substring body 0 break-index)
+ (assq-delete-all :epilogue (copy-alist params))))
+ ;; todo: replace vars. :-( → set vars break when doing something like $file.txt
+ (control-body (org-babel-spice-trim-body (substring body break-index)))
+ (full-control-body (if control-body
+ (org-babel-expand-body:generic
+ control-body
+ (assq-delete-all :prologue (copy-alist params))
+ (org-babel-variable-assignments:spice params))))
+ (circuit-file (if circuit-body (org-babel-temp-file "spice-body-" ".cir")))
+ (result))
+
+
+ (message (concat "circuit:\n" circuit-body))
+ (message (concat "\n-----\ncontrol:\n" control-body))
+
+ ;; Source circuit-body
+ (with-temp-file circuit-file (insert circuit-body))
+ (org-babel-spice-source session circuit-file)
+ ;; Run control-body
+ (setq result (org-babel-spice-evaluate session full-control-body result-type result-params))
+
+
+ ;; TODO deal with temporary files
+
+ ;;(org-babel-eval "ngspice -b " body)
+ ;; Write body to temp file & execute with ngspice comint buffer and ~source file~
+
+
+ ;; TODO read outputs from files
+
+ ;; TODO gnuplot options
+ (if (string= "yes" gnuplot)
+ nil ;return content(!) of gnuplot.plt for :post processing with gnuplot block?
+ nil ;return normal spice output
+ )
+ result
+ ))
+(defun org-babel-spice-source (buffer file)
+ "Source FILE in ngspice process running in BUFFER and return results."
+ (let ((body (concat "source " file)))
+ (org-babel-spice-evaluate buffer body 'value)))
+(defun org-babel-spice-evaluate (buffer body result-type &optional result-params)
+ "Use BUFFER running ngspice process to eval BODY and return results.
+ If RESULT-TYPE equals `output' return all outputs, if it equals
+ `value' return only value of last statement."
+ (let ((eoe-string (format "echo \"%s\"" org-babel-spice-eoe-indicator)))
+ (pcase result-type
+ (`output
+ ;; Force session to be ready
+ ;;(org-babel-comint-with-output
+ ;; (buffer org-babel-spice-eoe-indicator t eoe-string)
+ ;; (insert eoe-string) (comint-send-input nil t))
+ ;; Eval body
+ (replace-regexp-in-string
+ "^\\(ngspice [0-9]+ -> \\)*" ""
+ (mapconcat
+ #'identity
+ (butlast
+ (cdr
+ (split-string
+ (mapconcat
+ #'org-trim
+ (org-babel-comint-with-output (buffer org-babel-spice-eoe-indicator t body)
+ (mapcar (lambda (line)
+ (insert (org-babel-chomp line)) (comint-send-input nil t))
+ (list body
+ eoe-string
+ "\n")))
+ "\n") "[\r\n]")) 2) "\n"))
+ )
+ (`value
+ (let ((tmp-file (org-babel-temp-file "spice-")))
+ (org-babel-comint-with-output
+ (buffer org-babel-spice-eoe-indicator t body)
+ (mapcar
+ (lambda (line)
+ (insert (org-babel-chomp line)) (comint-send-input nil t))
+ (append (list body)
+ (list (format "!! > %s" tmp-file)
+ (format "echo \"%s\"" org-babel-spice-eoe-indicator)
+ )))
+ (comint-send-input nil t))
+ ;; split result to output multiple comma separated vars as table
+ (let ((result (split-string (org-babel-chomp (org-babel-eval-read-file tmp-file)) ",")))
+ (if (cdr result)
+ result
+ (car result))
+ )))
+ ;;todo: add "smart" result type to display measurements (or echos?) & plot filenames
+ )))
+
+(provide 'ob-spice)
+;;; ob-spice.el ends here
#+END_SRC
* Tests
@@ -423,45 +423,45 @@ echo "Hello World"
#+BEGIN_SRC spice :var file="/tmp/spice_test" :session spicetest :results value
- ,*Time Constant Measurement
- r1 1 0 10k
- c1 1 0 1p
-
- .IC V(1)=1
- .tran 1n 0.1u
- .print tran v(1)
- .end
-
- .control
- run
- set gnuplot_terminal=png
- ,*gnuplot $file v(1)
- meas tran value_at_tau find V(1) at=1e-8
- meas tran value_at_five_tau find V(1) at=5e-8
- echo value_at_tau = "$&value_at_tau" > $file.txt
- ,* Any better way to write one value of vector to a file??
- echo $&value_at_tau ,$&value_at_five_tau
- .endc
+,*Time Constant Measurement
+r1 1 0 10k
+c1 1 0 1p
+
+.IC V(1)=1
+.tran 1n 0.1u
+.print tran v(1)
+.end
+
+.control
+run
+set gnuplot_terminal=png
+,*gnuplot $file v(1)
+meas tran value_at_tau find V(1) at=1e-8
+meas tran value_at_five_tau find V(1) at=5e-8
+echo value_at_tau = "$&value_at_tau" > $file.txt
+,* Any better way to write one value of vector to a file??
+echo $&value_at_tau ,$&value_at_five_tau
+.endc
#+END_SRC
#+RESULTS:
| 0.36798 | 0.00671732 |
#+BEGIN_SRC spice :var file="/tmp/spice_test2" :session spicetest :results output
- ,*Virtual Ground Test: opamp gain = 1000
- vin in 0 dc 0V sin(0 .1 100Hz)
- r1 in inn 10k
- r2 inn out 10k
- EOpamp out 0 0 inn 1000
- .tran 0.1ms 0.05s
- .print tran v(in)
- .meas tran vtest find v(in) at=0.04e-3
- .end
- .control
- run
- set gnuplot_terminal=png
- gnuplot $file v(in) v(out) v(inn)
- .endc
+,*Virtual Ground Test: opamp gain = 1000
+vin in 0 dc 0V sin(0 .1 100Hz)
+r1 in inn 10k
+r2 inn out 10k
+EOpamp out 0 0 inn 1000
+.tran 0.1ms 0.05s
+.print tran v(in)
+.meas tran vtest find v(in) at=0.04e-3
+.end
+.control
+run
+set gnuplot_terminal=png
+gnuplot $file v(in) v(out) v(inn)
+.endc
#+END_SRC
#+RESULTS: