Compare commits

..

8 Commits

Author SHA1 Message Date
Nikolay Puzanov
3ccd9d2d22 Fix title absent 2024-08-11 23:20:44 +03:00
Nikolay Puzanov
53408f7bdf Exact numbers is not supported by gnuplot. Fix it 2024-06-06 12:48:19 +03:00
Nikolay Puzanov
1487382015 Initial add gnuplot plotting 2024-06-05 22:57:38 +03:00
Nikolay Puzanov
dfff93795e Add TODO 2023-02-20 15:16:05 +03:00
Nikolay Puzanov
ddeb6374f3 Rename string-split-str argument 2023-02-02 18:03:54 +03:00
Nikolay Puzanov
4fb8185bdc Add function for split of string by string delimiter 2023-02-02 18:00:16 +03:00
Nikolay Puzanov
f9f22f4f31 Add optional signal filter predicate 2022-12-07 12:51:17 +03:00
Nikolay Puzanov
7a88f6a05f Fix bug 2022-12-07 12:50:59 +03:00
3 changed files with 149 additions and 15 deletions

View File

@@ -19,6 +19,7 @@
has-duplicates? find-duplicates
insert-between
string-replace-text
string-split-str
string-split-trim
get-word
substitute
@@ -220,6 +221,18 @@
(substitute str template-format subst-list))
ls)))
;;; Split the string STR into a list of the substrings delimited by DELIMITER
(define (string-split-str str delimiter)
(if (string-null? str)
'()
(let ((didx (string-contains str delimiter)))
(if didx
(cons (substring str 0 didx)
(string-split-str
(substring str (+ didx (string-length delimiter)))
delimiter))
(list str)))))
;;; Split string and remove empty itemes
(define (string-split-trim str pred?)
(remove string-null?

117
embddr/gnuplot.scm Normal file
View File

@@ -0,0 +1,117 @@
(define-module (embddr gnuplot))
(import (srfi srfi-1)
(srfi srfi-26)
(ice-9 popen)
(ice-9 format))
(export make-plot-xy
plot)
;;;
;;; println
;;;
(define (>> . args)
(when (not (null? args))
(apply format (cons #t args)))
(newline))
;;;
;;; Call app and write to stdin
;;;
(define (with-output-to-spawn cmd f)
(let ((port (open-output-pipe cmd)))
(with-output-to-port port f)
(force-output port)
(close-pipe port)))
;;;
;;; Make plot
;;;
(define* (make-plot-xy #:key
(x #f) ; abscissa
y ; ordinate
(title #f)
(with "lines"))
(let ((x (or x (iota (length y)))))
(if (not (= (length x)
(length y)))
(error "Length of X and Y lists is not equal")
(lambda (key)
(case key
((x) x)
((y) y)
((title) (or title ""))
((with) with)
(else #f))))))
;;;
;;; Plot plots
;;;
(define* (plot plots
#:key
(xlabel #f)
(ylabel #f)
(title #f)
(font "Iosevka")
(font-size 11))
(let ((plots (if (list? plots) plots `(,plots))))
(if (not (apply = (map (lambda (plot) (length (plot 'x))) plots)))
(error "Length of plots is not equal")
(let* ((file-port (mkstemp "/tmp/plot-data-XXXXXX" "w"))
(file-name (port-filename file-port)))
;; Write data to temporary file
(with-output-to-port file-port
(lambda ()
(for-each
(cut >> <>)
(map (lambda (sample) (string-join (map
(lambda (x) (number->string
(exact->inexact x)))
sample) " "))
(apply zip (cons ((first plots) 'x)
(map (lambda (plot) (plot 'y)) plots)))))))
(close-port file-port)
;; Call gnuplot
(with-output-to-spawn
"gnuplot"
(lambda ()
(>> "set term x11 enhanced font '~a,~a' persist size 640,480" font font-size)
;; (>> "set output '~a'" "draw.png")
(>> "set grid")
(when xlabel (>> "set xlabel '~a'" xlabel))
(when ylabel (>> "set ylabel '~a'" ylabel))
(when title (>> "set title '~a'" title))
(>> "plot ~a"
(string-join
(map (lambda (plot n)
(format #f "'~a' using 1:~a with ~a~a"
file-name
n
(plot 'with)
(if (plot 'title)
(format #f " title '~a'" (plot 'title))
"")))
plots
(iota (length plots) 2))
",\\\n"))))
;; Delete temporary file
(delete-file file-name)))))
;; (define (main args)
;; (plot
;; `(,(make-plot-xy
;; #:x (iota 17 20 -1)
;; #:y '(1 2 3 4 3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1)
;; #:title "plot A"
;; #:with "lines lw 2")
;; ,(make-plot-xy
;; #:x (iota 17 20 -1)
;; #:y (map (cut * -0.5 <>) '(1 2 3 4 3 2 1 0 -1 -2 -3 -4 -3 -2 -1 0 1))
;; #:title "plot B"
;; #:with "lines"))))

View File

@@ -123,8 +123,9 @@
;;;
;;; Parse VCD header
;;; Returns values of timescale and association list (tag . (<signal> ...))
;;; Predicate: (need-signal? <signal>)
;;;
(define (read-header port)
(define* (read-header port need-signal?)
(let next-cmd ((timescale #f)
(scope '())
(tags+signals '()))
@@ -138,6 +139,7 @@
((string-ci= name "$timescale")
(next-cmd (parse-timescale (second cmd)) scope tags+signals))
;; TODO: Add scope type (make scope as pair '(scope-type . scope-name))
((string-ci= name "$scope")
(next-cmd timescale (cons (third cmd) scope) tags+signals))
@@ -149,18 +151,20 @@
(else 'bits)))
(width (string->number (third cmd)))
(tag (fourth cmd))
(vname (string-concatenate
(insert-between
(cddddr cmd) " "))))
(name (string-concatenate
(insert-between
(cddddr cmd) " "))))
(next-cmd timescale scope
(let ((tag+sig (assoc tag tags+signals))
(sig (signal-new (reverse scope)
vname type width '())))
(if tag+sig
(begin
(set-cdr! tag+sig (cons sig (cdr tag+sig)))
tags+signals)
(cons (cons tag `(,sig)) tags+signals))))))
(let ((sig (signal-new (reverse scope)
name type width '())))
(if (need-signal? sig)
(let ((tag+sig (assoc tag tags+signals)))
(if tag+sig
(begin
(set-cdr! tag+sig (cons sig (cdr tag+sig)))
tags+signals)
(cons (cons tag `(,sig)) tags+signals)))
tags+signals)))))
((string-ci= "$upscope" (first cmd))
(next-cmd timescale (cdr scope) tags+signals))
@@ -258,7 +262,7 @@
;; Update register values list
(let ((sigs (hash-table-ref/default tags+signals-ht tag #f)))
(unless (null? sigs)
(when sigs
(for-each
(lambda (sig)
(signal-add!
@@ -299,9 +303,9 @@
;;; Parse whole VCD
;;; Returns list of time stamps and list of lists (<signal> valuse ...)
;;;
(define (vcd-parse port)
(define* (vcd-parse port #:optional (need-signal? (lambda (s) #t)))
(call-with-values
(lambda () (read-header port))
(lambda () (read-header port need-signal?))
(cut read-data port <...>)))
;;;