From c4fc026af993b8bdded064ee8bf8d9c3d05f267a Mon Sep 17 00:00:00 2001 From: Holger Vogt Date: Sat, 28 Feb 2026 16:15:28 +0100 Subject: [PATCH] Add command 'linearnp np=1024 vec1 vec2' linearises tran output on given number of time points. If none is given, 2^n new points are chosen, next to but smaller than original number of time points. --- src/frontend/commands.c | 4 ++ src/frontend/linear.c | 143 ++++++++++++++++++++++++++++++++++++++++ src/frontend/linear.h | 1 + 3 files changed, 148 insertions(+) diff --git a/src/frontend/commands.c b/src/frontend/commands.c index 3de1ba6fb..e61aa069e 100644 --- a/src/frontend/commands.c +++ b/src/frontend/commands.c @@ -642,6 +642,10 @@ struct comm spcp_coms[] = { { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, " [ vec ... ] : Convert plot into one with linear scale." } , + { "linearnp", com_linearnp, FALSE, FALSE, + { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, + NULL, + " [ vec ... ] : Convert plot into one with linear scale, number of points given." }, { "cutout", com_cutout, FALSE, FALSE, { 040000, 040000, 040000, 040000 }, E_DEFHMASK, 0, LOTS, NULL, diff --git a/src/frontend/linear.c b/src/frontend/linear.c index 3fc903824..7148b8f08 100644 --- a/src/frontend/linear.c +++ b/src/frontend/linear.c @@ -130,6 +130,149 @@ com_linearize(wordlist *wl) } } +/* Interpolate all the vectors in a plot to a linear time scale, which + * we determine by looking at the transient parameters in the CKT struct. + * If no circuit is loaded, e.g. because the 'load' command has been used + * to obtain data, try to get parameters from scale vector. + * Interpolation may be restricted to only a region of the input vector, + * thus creating a cutout of the original vector. The number of data points + * is given by parameter np=xx. + */ +void +com_linearnp(wordlist* wl) +{ + double tstart, tstop, tstep, d; + struct plot* new, * old; + struct dvec* newtime, * v; + struct dvec* oldtime; + struct dvec* lin; + int expo, len, i; + wordlist* wlnew; + + if (!plot_cur || !plot_cur->pl_typename || !ciprefix("tran", plot_cur->pl_typename)) { + fprintf(cp_err, "Error: plot must be a transient analysis\n"); + return; + } + if (!plot_cur->pl_dvecs || !plot_cur->pl_scale) { + fprintf(cp_err, "Error: no vectors available\n"); + return; + } + if (!isreal(plot_cur->pl_scale)) { + fprintf(cp_err, "Error: non-real time scale for %s\n", + plot_cur->pl_typename); + return; + } + + /* check if circuit is loaded and TSTART, TSTOP, TSTEP are available + if no circuit is loaded, but vectors are available, obtain + start, stop, step data from scale vector */ + if (!ft_curckt || !ft_curckt->ci_ckt || + !if_tranparams(ft_curckt, &tstart, &tstop, &tstep)) { + fprintf(cp_err, + "Warning: Can't get transient parameters from circuit.\n" + " Use transient analysis scale vector data instead.\n"); + int length = plot_cur->pl_scale->v_length; + if (length < 1) { + fprintf(cp_err, "Error: no data in vector\n"); + return; + } + tstart = plot_cur->pl_scale->v_realdata[0]; + tstop = plot_cur->pl_scale->v_realdata[length - 1]; + tstep = (tstop - tstart) / (double)length; + } + + /* if this plot contains special vectors lin-tstart, lin-tstop or lin-tstep, use these instead */ + lin = vec_fromplot("lin-tstart", plot_cur); + if (lin) { + fprintf(cp_out, "linearize tstart is set to: %8e\n", lin->v_realdata[0]); + tstart = lin->v_realdata[0]; + } + + lin = vec_fromplot("lin-tstop", plot_cur); + if (lin) { + fprintf(cp_out, "linearize tstop is set to: %8e\n", lin->v_realdata[0]); + tstop = lin->v_realdata[0]; + } + + lin = vec_fromplot("lin-tstep", plot_cur); + if (lin) { + fprintf(cp_out, "linearize tstep is set to: %8e\n", lin->v_realdata[0]); + tstep = lin->v_realdata[0]; + } + + /* finally check if tstart, tstop and tstep are reasonable */ + if (((tstop - tstart) * tstep <= 0.0) || ((tstop - tstart) < tstep)) { + fprintf(cp_err, + "Error: bad parameters -- start = %G, stop = %G, step = %G\n", + tstart, tstop, tstep); + return; + } + old = plot_cur; + oldtime = old->pl_scale; + new = plot_alloc("transient"); + new->pl_name = tprintf("%s (linearized)", old->pl_name); + new->pl_title = copy(old->pl_title); + new->pl_date = copy(old->pl_date); + new->pl_next = plot_list; + plot_new(new); + plot_setcur(new->pl_typename); + plot_list = new; + + /* default number of points */ + expo = (int)(log2((tstop - tstart) / tstep)); + len = 1 << expo; + + wlnew = wl; + /* get the new length from 'np=xx' */ + while (wlnew) { + char* para = wlnew->wl_word; + if (ciprefix("np=", para)) { + para += 3; + len = atoi(para); + break; + } + wlnew = wlnew->wl_next; + } + + tstep = (tstop - tstart) / (double)len; + + newtime = dvec_alloc(copy(oldtime->v_name), + oldtime->v_type, + oldtime->v_flags | VF_PERMANENT, + len, NULL); + + newtime->v_plot = new; + for (i = 0, d = tstart; i < len; i++, d += tstep) + newtime->v_realdata[i] = d; + new->pl_scale = new->pl_dvecs = newtime; + + if (wl) { + while (wl) { + if (ciprefix("np=", wl->wl_word)) { + wl = wl->wl_next; + continue; + } + v = vec_fromplot(wl->wl_word, old); + if (!v) { + fprintf(cp_err, "Error: no such vector %s\n", + wl->wl_word); + wl = wl->wl_next; + continue; + } + lincopy(v, newtime->v_realdata, len, oldtime); + wl = wl->wl_next; + } + } + else { + for (v = old->pl_dvecs; v; v = v->v_next) { + if (v == old->pl_scale) + continue; + lincopy(v, newtime->v_realdata, len, oldtime); + } + } +} + + /* Cut out part of tran vectors from cut-tstart to cut-tstop and copy these into a new plot. A new scale vector 'time' will be there as well. Vectors diff --git a/src/frontend/linear.h b/src/frontend/linear.h index d1f731721..6fe15af2e 100644 --- a/src/frontend/linear.h +++ b/src/frontend/linear.h @@ -7,6 +7,7 @@ #define ngspice_LINEAR_H void com_linearize(wordlist *wl); +void com_linearnp(wordlist *wl); void com_cutout(wordlist *wl);