This commit is contained in:
Meisam 2026-07-28 17:25:26 +02:00 committed by Holger Vogt
parent 8cc60336ec
commit 5970997cc5
5 changed files with 76 additions and 0 deletions

View File

@ -57,6 +57,8 @@ libfte_la_SOURCES = \
com_qpss.h \
com_qpac.c \
com_qpac.h \
com_qpxf.c \
com_qpxf.h \
com_qpnoise.c \
com_qpnoise.h \
com_hbosc.c \

View File

@ -9,6 +9,7 @@ void com_alterparam(wordlist *wl);
void com_optimize(wordlist *wl); /* Enhancement-130 */
void com_qpss(wordlist *wl); /* Enhancement-133 */
void com_qpac(wordlist *wl); /* Enhancement-137 */
void com_qpxf(wordlist *wl); /* Enhancement-141 */
void com_qpnoise(wordlist *wl); /* Enhancement-138 */
void com_hbosc(wordlist *wl); /* Enhancement-140 */
void com_phasenoise(wordlist *wl); /* Enhancement-140 */

View File

@ -440,6 +440,10 @@ struct comm spcp_coms[] = {
{ 040, 040, 040, 040 }, E_DEFHMASK, 1, LOTS,
NULL,
"f_in : two-tone small-signal QPAC -- response at sidebands f_in+k1f1+k2f2 around the `qpss ... hb` operating point." },
{ "qpxf", com_qpxf, TRUE, FALSE, /* Enhancement-141 */
{ 040, 040, 040, 040 }, E_DEFHMASK, 2, LOTS,
NULL,
"output_node f_in : two-tone QPXF -- transfer from each input sideband f_in+k1f1+k2f2 to the output, around the `qpss ... hb` operating point." },
{ "qpnoise", com_qpnoise, TRUE, FALSE, /* Enhancement-138 */
{ 040, 040, 040, 040 }, E_DEFHMASK, 2, LOTS,
NULL,

View File

@ -478,6 +478,7 @@ struct hbspectrum {
extern int HBanalyze(CKTcircuit *, double f0, int K, int P, int maxiter, double tol, int verbose, struct hbspectrum *out); /* E-134; E-209 out */
extern int QPSShb(CKTcircuit *, double f1, double f2, int K1, int K2, int P1, int P2, int maxiter, double tol, int verbose); /* E-136 */
extern int QPACanalyze(CKTcircuit *, double f_in, int verbose); /* E-137 */
extern int QPXFanalyze(CKTcircuit *, int outNode, double f_in, int verbose); /* E-141 */
extern int QPnoiseAnalyze(CKTcircuit *, int outNode, double f_in, int cyclo, int verbose); /* E-138 / -139 */
extern int HBOSCanalyze(CKTcircuit *, int oscNode, int K, int P, double f0seed, double ampseed, int maxiter, double tol, int verbose); /* E-140 */
extern int PhaseNoiseAnalyze(CKTcircuit *, double fstart, double fstop, int npts, int verbose); /* E-140 */

View File

@ -2425,6 +2425,74 @@ QPnoiseAnalyze(CKTcircuit *ckt, int outNode, double f_in, int cyclo, int verbose
}
/* ======================================================================
* Enhancement-141: two-tone small-signal QPXF -- the quasi-periodic transfer
* function, the ADJOINT of QPAC (Enhancement-137). One adjoint solve of the
* 2-D conversion matrix, H^T Psi = e_{out,(0,0)}, gives the transimpedance
* Psi_{(k1,k2),j} from an injection at (node j, sideband (k1,k2)) to the
* output at sideband (0,0). Dotting each sideband block of Psi with the
* netlist AC-source pattern B0 gives the transfer from that input sideband to
* the output. By reciprocity the (0,0) transfer equals the QPAC response at
* the output node -- a cross-check that pins the adjoint solve (cf. PXF/PAC,
* Enhancement-125). Reuses qp_solve_adjoint (built for QPnoise, E-138).
* ====================================================================== */
int
QPXFanalyze(CKTcircuit *ckt, int outNode, double f_in, int verbose)
{
struct qp_harm *hd = qpss_hb_saved;
int N, Ntot, j, hi, numNames, error, k1, k2, ord;
IFuid *nameList = NULL;
double *Psr, *Psi;
if (!hd) {
fprintf(stderr, "qpxf: no QPSS operating point -- run `qpss <expr> <f1> <f2> hb` first.\n");
return E_NOTFOUND;
}
N = hd->N; Ntot = hd->Ntot;
if (outNode <= 0 || outNode > N) { fprintf(stderr, "qpxf: bad output node.\n"); return E_PARMVAL; }
(void) verbose;
Psr = TMALLOC(double, Ntot); Psi = TMALLOC(double, Ntot);
if (qp_solve_adjoint(hd, f_in, outNode, Psr, Psi)) {
fprintf(stderr, "qpxf: singular conversion matrix.\n");
FREE(Psr); FREE(Psi); return E_SINGULAR;
}
error = CKTnames(ckt, &numNames, &nameList);
fprintf(stdout,
"\nQPXF: two-tone transfer function to node %s (f_in = %g Hz, f1 = %g Hz, f2 = %g Hz)\n"
" (k1,k2) input sideband f_in+k1f1+k2f2 [Hz] |H| phase [deg]\n",
(!error && (outNode-1) < numNames) ? (const char *) nameList[outNode-1] : "?",
f_in, hd->f1, hd->f2);
for (ord = 0; ord <= hd->K1 + hd->K2; ord++)
for (k1 = -hd->K1; k1 <= hd->K1; k1++)
for (k2 = -hd->K2; k2 <= hd->K2; k2++) {
double fsb, hr = 0.0, hii = 0.0;
if (abs(k1) + abs(k2) != ord) continue;
hi = (k1 + hd->K1) * (2*hd->K2 + 1) + (k2 + hd->K2);
/* transfer from input at this sideband to the output: Psi_block . B0
* (AC-source pattern), or Psi at the injection node if no AC source */
if (hd->has_src) {
for (j = 0; j < N; j++) {
double pr = Psr[(size_t)hi*(size_t)N + (size_t)j];
double pi = Psi[(size_t)hi*(size_t)N + (size_t)j];
hr += pr*hd->B0r[j] - pi*hd->B0i[j];
hii += pr*hd->B0i[j] + pi*hd->B0r[j];
}
} else {
hr = Psr[(size_t)hi*(size_t)N + 0];
hii = Psi[(size_t)hi*(size_t)N + 0];
}
fsb = f_in + k1*hd->f1 + k2*hd->f2;
fprintf(stdout, " (%2d,%2d) %18.6e %14.6e %10.3f\n",
k1, k2, fsb, hypot(hr, hii), atan2(hii, hr) * 180.0/M_PI);
}
if (nameList) tfree(nameList);
FREE(Psr); FREE(Psi);
return OK;
}
/* ======================================================================
* Enhancement-140: oscillator phase noise.
*