Add missing files

This commit is contained in:
Meisam 2026-07-30 11:08:14 +02:00 committed by Holger Vogt
parent 12012fc35c
commit 38c6156e6c
2 changed files with 104 additions and 0 deletions

97
src/frontend/com_qpxf.c Normal file
View File

@ -0,0 +1,97 @@
/**********
Enhancement-141: two-tone small-signal QPXF (quasi-periodic transfer function) --
`qpxf <output_node> <f_in>`.
The adjoint of QPAC: around the QPSS operating point retained by a prior
`qpss <expr> <f1> <f2> hb`, one adjoint solve of the 2-D conversion matrix gives the
transfer from an input at every sideband f_in + k1*f1 + k2*f2 to the chosen output at
f_in. By reciprocity the sideband-(0,0) transfer equals the QPAC response at that node.
The engine is QPXFanalyze() (spicelib/analysis/dcpss.c); this command resolves the
output node and runs it.
**********/
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/cktdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/fteext.h"
#include "ngspice/wordlist.h"
#include "ngspice/cpextern.h"
#include "com_qpxf.h"
static double qpxfnum(const char *w)
{
char *s = (char *) w;
double v = 0.0;
if (ft_numparse(&s, FALSE, &v) < 0)
v = atof(w);
return v;
}
static int qpxf_node(CKTcircuit *ckt, const char *name)
{
int numNames = 0, i, num = 0;
IFuid *nameList = NULL;
if (CKTnames(ckt, &numNames, &nameList) != OK || !nameList)
return 0;
for (i = 0; i < numNames; i++)
if (nameList[i] && strcmp((const char *) nameList[i], name) == 0) { num = i + 1; break; }
tfree(nameList);
return num;
}
void
com_qpxf(wordlist *wl)
{
CKTcircuit *ckt;
double f_in;
int outNode, verbose, err;
if (!ft_curckt || !ft_curckt->ci_ckt) {
fprintf(cp_err, "Error: qpxf: there is no circuit loaded.\n");
return;
}
ckt = ft_curckt->ci_ckt;
if (!wl || !wl->wl_next) {
fprintf(cp_err, "Usage: qpxf <output_node> <f_in> (run `qpss <expr> <f1> <f2> hb` first)\n");
return;
}
outNode = qpxf_node(ckt, wl->wl_word);
if (outNode <= 0) { fprintf(cp_err, "Error: qpxf: unknown output node '%s'.\n", wl->wl_word); return; }
{ /* sweep form: qpxf <out> <dec|oct|lin> <N> <fstart> <fstop> -> xf/xf_conv plot */
extern int qp_steptype(const char *w);
extern int qp_sweep_maxpts(int stepType, int np, double fstart, double fstop);
extern void qp_emit_plot(const char *plotname, const char *title, double *freqs, int npts,
char **vnames, int nvec, double *data);
int st = qp_steptype(wl->wl_next->wl_word);
if (st >= 0) {
double fstart, fstop, *freqs, *data; int np, npts, maxpts;
char *vn[2] = { (char*)"xf", (char*)"xf_conv" };
wordlist *w = wl->wl_next->wl_next;
if (!w || !w->wl_next || !w->wl_next->wl_next) {
fprintf(cp_err, "Usage: qpxf <output_node> <dec|oct|lin> <N> <fstart> <fstop>\n"); return;
}
np = (int) qpxfnum(w->wl_word);
fstart = qpxfnum(w->wl_next->wl_word);
fstop = qpxfnum(w->wl_next->wl_next->wl_word);
if (np < 1 || fstart <= 0.0 || fstop < fstart) { fprintf(cp_err, "Error: qpxf: bad sweep spec.\n"); return; }
maxpts = qp_sweep_maxpts(st, np, fstart, fstop);
freqs = TMALLOC(double, maxpts); data = TMALLOC(double, (size_t)maxpts*2);
npts = QPXFsweep(ckt, outNode, st, np, fstart, fstop, freqs, data);
if (npts > 0) {
qp_emit_plot("qpxf", "QPXF Analysis", freqs, npts, vn, 2, data);
fprintf(cp_out, "qpxf: swept %d points into a new plot (now current); `plot xf xf_conv` to view.\n", npts);
} else fprintf(cp_err, "qpxf: sweep did not complete.\n");
FREE(freqs); FREE(data);
return;
}
}
f_in = qpxfnum(wl->wl_next->wl_word);
if (f_in <= 0.0) { fprintf(cp_err, "Error: qpxf: need f_in > 0.\n"); return; }
verbose = cp_getvar("qpxf_verbose", CP_BOOL, NULL, 0);
err = QPXFanalyze(ckt, outNode, f_in, verbose ? 1 : 0);
if (err != OK)
fprintf(cp_err, "qpxf: quasi-periodic transfer function did not complete (error %d).\n", err);
}

7
src/frontend/com_qpxf.h Normal file
View File

@ -0,0 +1,7 @@
#ifndef ngspice_COM_QPXF_H
#define ngspice_COM_QPXF_H
/* Enhancement-141: two-tone small-signal QPXF (quasi-periodic transfer function). */
void com_qpxf(wordlist *wl);
#endif