pa-138
This commit is contained in:
parent
76aa690e13
commit
a40fe00924
|
|
@ -57,6 +57,8 @@ libfte_la_SOURCES = \
|
|||
com_qpss.h \
|
||||
com_qpac.c \
|
||||
com_qpac.h \
|
||||
com_qpnoise.c \
|
||||
com_qpnoise.h \
|
||||
com_hb.c \
|
||||
com_hb.h \
|
||||
com_checkpoint.c \
|
||||
|
|
|
|||
|
|
@ -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_qpnoise(wordlist *wl); /* Enhancement-138 */
|
||||
void com_hb(wordlist *wl); /* Enhancement-134 */
|
||||
void com_savestate(wordlist *wl); /* Enhancement-131 */
|
||||
void com_loadstate(wordlist *wl); /* Enhancement-131 */
|
||||
|
|
|
|||
|
|
@ -0,0 +1,82 @@
|
|||
/**********
|
||||
Enhancement-138: two-tone small-signal QPnoise (quasi-periodic noise) --
|
||||
`qpnoise <output_node> <f_in>`.
|
||||
|
||||
Around the QPSS operating point retained by a prior `qpss <expr> <f1> <f2> hb`, folds
|
||||
every device's noise through the ADJOINT of the 2-D conversion matrix over all sidebands
|
||||
to the output at f_in -- the two-tone analogue of pnoise. A mixer/PA's device noise at
|
||||
each sideband f_in + k1*f1 + k2*f2 is converted (folded) to the output. The engine is
|
||||
QPnoiseAnalyze() (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 "ngspice/ifsim.h"
|
||||
|
||||
#include "com_qpnoise.h"
|
||||
|
||||
static double qpnnum(const char *w)
|
||||
{
|
||||
char *s = (char *) w;
|
||||
double v = 0.0;
|
||||
if (ft_numparse(&s, FALSE, &v) < 0)
|
||||
v = atof(w);
|
||||
return v;
|
||||
}
|
||||
|
||||
/* resolve a node name to its 1-based CKT node number via the name list, else 0 */
|
||||
static int qpn_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_qpnoise(wordlist *wl)
|
||||
{
|
||||
CKTcircuit *ckt;
|
||||
double f_in;
|
||||
int outNode, verbose, err;
|
||||
|
||||
if (!ft_curckt || !ft_curckt->ci_ckt) {
|
||||
fprintf(cp_err, "Error: qpnoise: there is no circuit loaded.\n");
|
||||
return;
|
||||
}
|
||||
ckt = ft_curckt->ci_ckt;
|
||||
|
||||
if (!wl || !wl->wl_next) {
|
||||
fprintf(cp_err, "Usage: qpnoise <output_node> <f_in> "
|
||||
"(run `qpss <expr> <f1> <f2> hb` first)\n");
|
||||
return;
|
||||
}
|
||||
outNode = qpn_node(ckt, wl->wl_word);
|
||||
if (outNode <= 0) {
|
||||
fprintf(cp_err, "Error: qpnoise: unknown output node '%s'.\n", wl->wl_word);
|
||||
return;
|
||||
}
|
||||
f_in = qpnnum(wl->wl_next->wl_word);
|
||||
if (f_in <= 0.0) {
|
||||
fprintf(cp_err, "Error: qpnoise: need f_in > 0.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
verbose = cp_getvar("qpnoise_verbose", CP_BOOL, NULL, 0);
|
||||
err = QPnoiseAnalyze(ckt, outNode, f_in, verbose ? 1 : 0);
|
||||
if (err != OK)
|
||||
fprintf(cp_err, "qpnoise: quasi-periodic noise did not complete (error %d).\n", err);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#ifndef ngspice_COM_QPNOISE_H
|
||||
#define ngspice_COM_QPNOISE_H
|
||||
|
||||
/* Enhancement-138: two-tone small-signal QPnoise (quasi-periodic noise). */
|
||||
void com_qpnoise(wordlist *wl);
|
||||
|
||||
#endif
|
||||
|
|
@ -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." },
|
||||
{ "qpnoise", com_qpnoise, TRUE, FALSE, /* Enhancement-138 */
|
||||
{ 040, 040, 040, 040 }, E_DEFHMASK, 2, LOTS,
|
||||
NULL,
|
||||
"output_node f_in : two-tone QPnoise -- output/input noise density at f_in, folding device noise over all sidebands around the `qpss ... hb` operating point." },
|
||||
{ "hb", com_hb, TRUE, FALSE, /* Enhancement-134 */
|
||||
{ 040, 040, 040, 040 }, E_DEFHMASK, 2, LOTS,
|
||||
NULL,
|
||||
|
|
|
|||
|
|
@ -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 QPnoiseAnalyze(CKTcircuit *, int outNode, double f_in, int verbose); /* E-138 */
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -2210,6 +2210,155 @@ QPACanalyze(CKTcircuit *ckt, double f_in, int verbose)
|
|||
}
|
||||
|
||||
|
||||
/* ======================================================================
|
||||
* Enhancement-138: two-tone small-signal QPnoise (quasi-periodic noise).
|
||||
* Around the QPSS operating point retained by `qpss ... hb`, fold every
|
||||
* device's noise through the ADJOINT of the 2-D conversion matrix over
|
||||
* all sidebands to the output at f_in -- the two-tone analogue of pnoise
|
||||
* (E-124). Each device's noise routine computes S*|dTransimp|^2 reading
|
||||
* the transimpedance from CKTrhs/CKTirhs, so loading the sideband-(k1,k2)
|
||||
* adjoint transfer into CKTrhs and summing over all harmonics folds the
|
||||
* noise exactly (mixer/PA folded noise). With no pump the conversion
|
||||
* matrix is block-diagonal, so only sideband (0,0) contributes and the
|
||||
* result reduces to ordinary .noise.
|
||||
* ====================================================================== */
|
||||
|
||||
/* Solve the ADJOINT 2-D conversion system H^T Psi = e_{out,(0,0)}. Psi then
|
||||
* holds, for every (node j, harmonic hi), the transfer from a unit injection at
|
||||
* (j,hi) to the output at sideband (0,0). Reuses qp_build_matrix (forward H) and
|
||||
* transposes in place. Returns 0 on success, 1 if singular. */
|
||||
static int
|
||||
qp_solve_adjoint(struct qp_harm *hd, double f_in, int outNode, double *Psr, double *Psi)
|
||||
{
|
||||
int Ntot = hd->Ntot, N = hd->N, i, j, i00, rc;
|
||||
double *Ar, *Ai;
|
||||
|
||||
Ar = TMALLOC(double, (size_t)Ntot * (size_t)Ntot);
|
||||
Ai = TMALLOC(double, (size_t)Ntot * (size_t)Ntot);
|
||||
qp_build_matrix(hd, f_in, Ar, Ai);
|
||||
for (i = 0; i < Ntot; i++) /* transpose in place: H -> H^T */
|
||||
for (j = i + 1; j < Ntot; j++) {
|
||||
size_t ij = (size_t)i*(size_t)Ntot + (size_t)j;
|
||||
size_t ji = (size_t)j*(size_t)Ntot + (size_t)i;
|
||||
double t;
|
||||
t = Ar[ij]; Ar[ij] = Ar[ji]; Ar[ji] = t;
|
||||
t = Ai[ij]; Ai[ij] = Ai[ji]; Ai[ji] = t;
|
||||
}
|
||||
memset(Psr, 0, (size_t)Ntot * sizeof(double));
|
||||
memset(Psi, 0, (size_t)Ntot * sizeof(double));
|
||||
i00 = hd->K1 * (2*hd->K2 + 1) + hd->K2;
|
||||
Psr[(size_t)i00 * (size_t)N + (size_t)(outNode - 1)] = 1.0;
|
||||
rc = pss_csolve(Ntot, Ar, Ai, Psr, Psi);
|
||||
FREE(Ar); FREE(Ai);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
QPnoiseAnalyze(CKTcircuit *ckt, int outNode, double f_in, int verbose)
|
||||
{
|
||||
struct qp_harm *hd = qpss_hb_saved;
|
||||
int N, Nh, Ntot, i, j, hi, i00;
|
||||
double *Psr, *Psi, *Xr, *Xi;
|
||||
double onoise = 0.0, inoise, gain2 = 1.0;
|
||||
NOISEAN nj;
|
||||
Ndata data;
|
||||
JOB *oldJob;
|
||||
|
||||
if (!hd) {
|
||||
fprintf(stderr, "qpnoise: no QPSS operating point -- run `qpss <expr> <f1> <f2> hb` first.\n");
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
N = hd->N; Nh = hd->Nh; Ntot = hd->Ntot;
|
||||
if (outNode <= 0 || outNode > N) {
|
||||
fprintf(stderr, "qpnoise: bad output node.\n");
|
||||
return E_PARMVAL;
|
||||
}
|
||||
i00 = hd->K1 * (2*hd->K2 + 1) + hd->K2;
|
||||
|
||||
/* bias the devices at the QPSS operating point (phase (0,0) sample: v = sum of
|
||||
* all harmonic coefficients) so each noise PSD is at the periodic bias. */
|
||||
for (j = 1; j <= N; j++) {
|
||||
double v = 0.0;
|
||||
for (hi = 0; hi < Nh; hi++) v += hd->Vr[(size_t)hi*(size_t)N + (size_t)(j-1)];
|
||||
ckt->CKTrhsOld[j] = v;
|
||||
}
|
||||
ckt->CKTrhsOld[0] = 0.0;
|
||||
ckt->CKTmode = (ckt->CKTmode & MODEUIC) | MODEDCOP | MODEINITSMSIG;
|
||||
CKTload(ckt);
|
||||
|
||||
/* minimal NOISEAN context (device noise routines cast CKTcurJob to NOISEAN*) */
|
||||
memset(&nj, 0, sizeof(nj));
|
||||
nj.NstartFreq = f_in;
|
||||
nj.NstopFreq = f_in;
|
||||
nj.NnumSteps = 1;
|
||||
nj.NstpType = 0;
|
||||
nj.NStpsSm = 0;
|
||||
nj.JOBname = "qpnoise";
|
||||
memset(&data, 0, sizeof(data));
|
||||
data.prtSummary = FALSE;
|
||||
oldJob = ckt->CKTcurJob;
|
||||
ckt->CKTcurJob = (JOB *) &nj;
|
||||
for (i = 0; i < DEVmaxnum; i++)
|
||||
if (DEVices[i] && DEVices[i]->DEVnoise && ckt->CKThead[i]) {
|
||||
double dummy = 0.0;
|
||||
DEVices[i]->DEVnoise(N_DENS, N_OPEN, ckt->CKThead[i], ckt, &data, &dummy);
|
||||
}
|
||||
|
||||
Psr = TMALLOC(double, Ntot); Psi = TMALLOC(double, Ntot);
|
||||
Xr = TMALLOC(double, Ntot); Xi = TMALLOC(double, Ntot);
|
||||
|
||||
/* adjoint transfer from every (node, harmonic) to the output at (0,0), then
|
||||
* fold each device's noise density over all sidebands. */
|
||||
data.freq = f_in; data.delFreq = 0.0; data.prtSummary = FALSE;
|
||||
if (qp_solve_adjoint(hd, f_in, outNode, Psr, Psi) == 0) {
|
||||
for (hi = 0; hi < Nh; hi++) {
|
||||
double dens = 0.0;
|
||||
size_t blk = (size_t)hi * (size_t)N;
|
||||
for (j = 1; j <= N; j++) {
|
||||
ckt->CKTrhs[j] = Psr[blk + (size_t)(j-1)];
|
||||
ckt->CKTirhs[j] = Psi[blk + (size_t)(j-1)];
|
||||
}
|
||||
ckt->CKTrhs[0] = 0.0; ckt->CKTirhs[0] = 0.0;
|
||||
for (i = 0; i < DEVmaxnum; i++)
|
||||
if (DEVices[i] && DEVices[i]->DEVnoise && ckt->CKThead[i])
|
||||
DEVices[i]->DEVnoise(N_DENS, N_CALC, ckt->CKThead[i], ckt, &data, &dens);
|
||||
onoise += dens; /* sum device noise over sidebands */
|
||||
}
|
||||
}
|
||||
|
||||
/* input-referred: divide by the source->output conversion gain at (0,0) */
|
||||
if (hd->has_src) {
|
||||
double *Ar = TMALLOC(double, (size_t)Ntot*(size_t)Ntot);
|
||||
double *Ai = TMALLOC(double, (size_t)Ntot*(size_t)Ntot);
|
||||
qp_build_matrix(hd, f_in, Ar, Ai);
|
||||
memset(Xr, 0, (size_t)Ntot*sizeof(double));
|
||||
memset(Xi, 0, (size_t)Ntot*sizeof(double));
|
||||
for (i = 0; i < N; i++) {
|
||||
Xr[(size_t)i00*(size_t)N + (size_t)i] = hd->B0r[i];
|
||||
Xi[(size_t)i00*(size_t)N + (size_t)i] = hd->B0i[i];
|
||||
}
|
||||
if (pss_csolve(Ntot, Ar, Ai, Xr, Xi) == 0) {
|
||||
size_t oidx = (size_t)i00*(size_t)N + (size_t)(outNode-1);
|
||||
gain2 = Xr[oidx]*Xr[oidx] + Xi[oidx]*Xi[oidx];
|
||||
}
|
||||
FREE(Ar); FREE(Ai);
|
||||
}
|
||||
inoise = onoise / MAX(gain2, N_MINGAIN);
|
||||
|
||||
ckt->CKTcurJob = oldJob;
|
||||
(void) verbose;
|
||||
fprintf(stdout,
|
||||
"\nQPnoise: two-tone output noise at f_in = %g Hz (folding %d sidebands, "
|
||||
"f1 = %g, f2 = %g)\n"
|
||||
" onoise density = %.6e V^2/Hz (%.6e V/sqrt(Hz))\n"
|
||||
" inoise density = %.6e (gain^2 = %.6e)\n",
|
||||
f_in, Nh, hd->f1, hd->f2, onoise, sqrt(onoise), inoise, gain2);
|
||||
|
||||
FREE(Psr); FREE(Psi); FREE(Xr); FREE(Xi);
|
||||
return OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
DCpss(CKTcircuit *ckt,
|
||||
int restart) /* forced restart flag */
|
||||
|
|
|
|||
|
|
@ -896,6 +896,7 @@
|
|||
<ClInclude Include="..\src\frontend\com_plot.h" />
|
||||
<ClInclude Include="..\src\frontend\com_pyplot.h" />
|
||||
<ClInclude Include="..\src\frontend\com_qpac.h" />
|
||||
<ClInclude Include="..\src\frontend\com_qpnoise.h" />
|
||||
<ClInclude Include="..\src\frontend\com_qpss.h" />
|
||||
<ClInclude Include="..\src\frontend\com_rehash.h" />
|
||||
<ClInclude Include="..\src\frontend\com_set.h" />
|
||||
|
|
@ -1515,6 +1516,7 @@
|
|||
<ClCompile Include="..\src\frontend\com_plot.c" />
|
||||
<ClCompile Include="..\src\frontend\com_pyplot.c" />
|
||||
<ClCompile Include="..\src\frontend\com_qpac.c" />
|
||||
<ClCompile Include="..\src\frontend\com_qpnoise.c" />
|
||||
<ClCompile Include="..\src\frontend\com_qpss.c" />
|
||||
<ClCompile Include="..\src\frontend\com_rehash.c" />
|
||||
<ClCompile Include="..\src\frontend\com_set.c" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue