loop analysis in development
This commit is contained in:
parent
17b1eec2aa
commit
9b53b5e663
|
|
@ -51,10 +51,14 @@ typedef struct {
|
|||
IFuid LOOPoutSrc;
|
||||
CKTnode *LOOPoutPos;
|
||||
CKTnode *LOOPoutNeg;
|
||||
CKTnode *LOOPrefNode;
|
||||
|
||||
double** LoopGain;
|
||||
double** iLoopGain;
|
||||
|
||||
unsigned LOOPinIV : 2;
|
||||
unsigned LOOPoutIV : 2;
|
||||
|
||||
unsigned LOOPnameGiven : 1;
|
||||
unsigned LOOPportnumGiven : 1;
|
||||
unsigned LOOPportnameGiven : 1;
|
||||
|
|
@ -62,6 +66,7 @@ typedef struct {
|
|||
unsigned LOOPoutSrcGiven : 1;
|
||||
unsigned LOOPoutPosGiven : 1;
|
||||
unsigned LOOPoutNegGiven : 1;
|
||||
unsigned LOOPrefNodeGiven : 1;
|
||||
} LOOPAN;
|
||||
|
||||
/* available step types: */
|
||||
|
|
@ -70,6 +75,11 @@ typedef struct {
|
|||
#define OCTAVE 2
|
||||
#define LINEAR 3
|
||||
|
||||
/* I or V flags */
|
||||
#define LOOP_IV_UNSET 0
|
||||
#define LOOP_IV_VOLTAGE 1
|
||||
#define LOOP_IV_CURRENT 2
|
||||
|
||||
/* defns. used in DsetParm */
|
||||
|
||||
#define LOOP_DEC 1
|
||||
|
|
@ -83,6 +93,7 @@ typedef struct {
|
|||
#define LOOP_PORTNAME 11
|
||||
#define LOOP_PORTNUM 12
|
||||
#define LOOP_DIR 13
|
||||
#define LOOP_REFNODE 14
|
||||
#define LOOP_PROBESRCNEG 15
|
||||
#define LOOP_PORTNAMENEG 16
|
||||
#define LOOP_PORTNUMNEG 17
|
||||
|
|
|
|||
|
|
@ -42,6 +42,44 @@ do { \
|
|||
|
||||
extern SPICEdev **DEVices;
|
||||
|
||||
static IFcomplex
|
||||
caddf(IFcomplex a, IFcomplex b)
|
||||
{
|
||||
IFcomplex r;
|
||||
r.real = a.real + b.real;
|
||||
r.imag = a.imag + b.imag;
|
||||
return r;
|
||||
}
|
||||
|
||||
static IFcomplex
|
||||
csubf(IFcomplex a, IFcomplex b)
|
||||
{
|
||||
IFcomplex r;
|
||||
r.real = a.real - b.real;
|
||||
r.imag = a.imag - b.imag;
|
||||
return r;
|
||||
}
|
||||
|
||||
static IFcomplex
|
||||
cmulf(IFcomplex a, IFcomplex b)
|
||||
{
|
||||
IFcomplex r;
|
||||
r.real = a.real*b.real - a.imag*b.imag;
|
||||
r.imag = a.real*b.imag + a.imag*b.real;
|
||||
return r;
|
||||
}
|
||||
|
||||
static IFcomplex
|
||||
cdivf(IFcomplex a, IFcomplex b)
|
||||
{
|
||||
IFcomplex r;
|
||||
double denom;
|
||||
denom = b.real*b.real + b.imag*b.imag;
|
||||
r.real = (a.real*b.real + a.imag*b.imag)/denom;
|
||||
r.imag = (a.imag*b.real - a.real*b.imag)/denom;
|
||||
return r;
|
||||
}
|
||||
|
||||
int
|
||||
LOOPpreset(CKTcircuit *ckt, JOB *anal)
|
||||
{
|
||||
|
|
@ -57,21 +95,22 @@ LOOPpreset(CKTcircuit *ckt, JOB *anal)
|
|||
int termidx;
|
||||
int error;
|
||||
int vtype;
|
||||
|
||||
printf("LOOPpreset with direction %d!\n", job->LOOPdirection);
|
||||
|
||||
|
||||
if(!job->LOOPrefNodeGiven)
|
||||
job->LOOPrefNode = NULL;
|
||||
|
||||
if(job->LOOPportnameGiven || job->LOOPportnumGiven)
|
||||
{
|
||||
inst = CKTfndDev(ckt, job->LOOPprobeSrc);
|
||||
if (!inst || inst->GENmodPtr->GENmodType < 0) {
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING,
|
||||
"Loop probe source %s not in circuit",
|
||||
"Loop probe source '%s' not in circuit",
|
||||
job->LOOPprobeSrc);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
dev = &DEVices[inst->GENmodPtr->GENmodType]->DEVpublic;
|
||||
if (inst->GENmodPtr->GENmodType == CKTtypelook("Vsource"))
|
||||
printf("Consider to specify the whole Vsource %s as loop probe\n", job->LOOPprobeSrc);
|
||||
printf("Consider to specify the whole Vsource '%s' as loop probe\n", job->LOOPprobeSrc);
|
||||
|
||||
if(job->LOOPportnameGiven) {
|
||||
for(termidx=0;termidx<(*(dev->numNames));termidx++) {
|
||||
|
|
@ -86,7 +125,7 @@ LOOPpreset(CKTcircuit *ckt, JOB *anal)
|
|||
return E_NOTFOUND;
|
||||
}
|
||||
/* now break the loop at terminal termidx */
|
||||
printf("Break the loop at device %s terminal %s\n", inst->GENname, dev->termNames[termidx]);
|
||||
printf("Loop analysis: Break the loop at device '%s' terminal '%s'\n", inst->GENname, dev->termNames[termidx]);
|
||||
node = CKTnum2nod(ckt, GENnode(inst)[termidx]);
|
||||
error = CKTmkVolt(ckt, &nodeinj, inst->GENname, "loopinj");
|
||||
if(error) return(error);
|
||||
|
|
@ -105,18 +144,81 @@ LOOPpreset(CKTcircuit *ckt, JOB *anal)
|
|||
ptemp.rValue = 0;
|
||||
error = CKTpName("dc",&ptemp,ckt,vtype,"probe",&probesrc);
|
||||
if(error) return(error);
|
||||
error = CKTbindNode(ckt,probesrc,job->LOOPdirection==2 ? 2 : 1,nodeinj);
|
||||
/* LOOPdirection is set later */
|
||||
error = CKTbindNode(ckt,probesrc,job->LOOPdirection==2 ? 1 : 1,nodeinj);
|
||||
if(error) return(error);
|
||||
error = CKTbindNode(ckt,probesrc,job->LOOPdirection==2 ? 1 : 2 ,node);
|
||||
error = CKTbindNode(ckt,probesrc,job->LOOPdirection==2 ? 2 : 2 ,node);
|
||||
if(error) return(error);
|
||||
error = CKTbindNode(ckt,inst,termidx+1,nodeinj); /* bindNode counts from 1 ! */
|
||||
ptemp.rValue = 0;
|
||||
job->LOOPprobeSrc = probesrc->GENname;
|
||||
job->LOOPportnameGiven = 0; /* don't mess if LOOPpreset is called a second time */
|
||||
job->LOOPportnumGiven = 0; /* idem */
|
||||
|
||||
job->LOOPinIV = LOOP_IV_UNSET;
|
||||
if(job->LOOPinSrcGiven) {
|
||||
GENinstance *insrc;
|
||||
insrc = CKTfndDev(ckt, job->LOOPinSrc);
|
||||
if (!insrc || insrc->GENmodPtr->GENmodType < 0) {
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING,
|
||||
"Transfer function source %s not in circuit",
|
||||
job->LOOPinSrc);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
if (insrc->GENmodPtr->GENmodType == CKTtypelook("Vsource"))
|
||||
job->LOOPinIV = LOOP_IV_VOLTAGE;
|
||||
else if (insrc->GENmodPtr->GENmodType == CKTtypelook("Isource")) {
|
||||
job->LOOPinIV = LOOP_IV_CURRENT;
|
||||
}
|
||||
}
|
||||
|
||||
job->LOOPoutIV = LOOP_IV_UNSET;
|
||||
if(job->LOOPoutSrcGiven) {
|
||||
GENinstance *outsrc;
|
||||
outsrc = CKTfndDev(ckt, job->LOOPoutSrc);
|
||||
if (!outsrc || outsrc->GENmodPtr->GENmodType < 0) {
|
||||
SPfrontEnd->IFerrorf (ERR_WARNING,
|
||||
"Transfer function source %s not in circuit",
|
||||
job->LOOPoutSrc);
|
||||
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
if (outsrc->GENmodPtr->GENmodType == CKTtypelook("Vsource"))
|
||||
job->LOOPoutIV = LOOP_IV_CURRENT;
|
||||
else if (outsrc->GENmodPtr->GENmodType == CKTtypelook("Isource")) {
|
||||
/* weird user, but let's kindly reformulate for him */
|
||||
job->LOOPinIV = LOOP_IV_VOLTAGE;
|
||||
job->LOOPoutPos = CKTnum2nod(ckt, GENnode(outsrc)[0]);
|
||||
job->LOOPoutNeg = CKTnum2nod(ckt, GENnode(outsrc)[1]);
|
||||
job->LOOPoutPosGiven = 1;
|
||||
job->LOOPoutNegGiven = 1;
|
||||
job->LOOPoutSrcGiven = 0;
|
||||
}
|
||||
} else if (job->LOOPoutPosGiven) {
|
||||
job->LOOPoutIV = LOOP_IV_VOLTAGE;
|
||||
}
|
||||
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
enum {
|
||||
LOOP_CURVE_T = 0,
|
||||
LOOP_CURVE_D,
|
||||
LOOP_CURVE_H,
|
||||
LOOP_CURVE_HINF,
|
||||
#if 1
|
||||
LOOP_CURVE_TN,
|
||||
LOOP_CURVE_DN,
|
||||
LOOP_CURVE_H0,
|
||||
LOOP_CURVE_D0,
|
||||
LOOP_CURVE_DP,
|
||||
LOOP_CURVE_TP,
|
||||
#endif
|
||||
/* insert new curves here */
|
||||
LOOP_NCURVES
|
||||
} eLOOPcurves;
|
||||
|
||||
int
|
||||
LOOPan(CKTcircuit *ckt, int restart)
|
||||
{
|
||||
|
|
@ -130,11 +232,12 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
double startkTime;
|
||||
double startTime;
|
||||
|
||||
double Vy[3],Iy[3];
|
||||
double iVy[3],iIy[3];
|
||||
double phasemargin;
|
||||
double gainmargin;
|
||||
double gainsq, phase, curTr;
|
||||
IFcomplex Vy[3],Iy[3];
|
||||
/*double iVy[3],iIy[3];*/
|
||||
IFcomplex Uout[3];
|
||||
double D, iD, T, iT;
|
||||
double phasemargin, gainmargin, maxgain, ugf, ipf;
|
||||
double gainsq, phase, curTr, pfreq;
|
||||
|
||||
int error;
|
||||
int numNames;
|
||||
|
|
@ -144,10 +247,47 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
runDesc *plot = NULL;
|
||||
GENinstance *inst = NULL;
|
||||
int size, i;
|
||||
int probe_brnum;
|
||||
|
||||
int branch_probe, branch_vin, branch_iout;
|
||||
int inPosNode, inNegNode;
|
||||
int dir;
|
||||
|
||||
static char* plot_curves[] = {
|
||||
"T", /* loop gain */
|
||||
"D", /* discrepency factor D = T/(1+T) */
|
||||
"H", /* transfert function */
|
||||
"Hinf", /* transfert function if loop gain was infinite */
|
||||
"Tn", /* "output-nulled" loop gain*/
|
||||
"Dn", /* 1/(1+Tn) */
|
||||
"H0", /* forward path transfert function with zero loop gain */
|
||||
"D0",
|
||||
"Dp",
|
||||
"Tp",
|
||||
};
|
||||
|
||||
|
||||
if(job->LOOPinSrcGiven) {
|
||||
if(job->LOOPinIV == LOOP_IV_CURRENT) {
|
||||
inst = CKTfndDev(ckt, job->LOOPinSrc);
|
||||
inPosNode = GENnode(inst)[0];
|
||||
inNegNode = GENnode(inst)[1];
|
||||
} else if (job->LOOPinIV == LOOP_IV_VOLTAGE) {
|
||||
branch_vin = CKTfndBranch(ckt, job->LOOPinSrc);
|
||||
#if 0
|
||||
/* not needed but set anyway */
|
||||
inst = CKTfndDev(ckt, job->LOOPinSrc);
|
||||
inPosNode = GENnode(inst)[0];
|
||||
inNegNode = GENnode(inst)[1];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if(job->LOOPoutSrcGiven) {
|
||||
if(job->LOOPoutIV == LOOP_IV_CURRENT)
|
||||
branch_iout = CKTfndBranch(ckt, job->LOOPoutSrc);
|
||||
}
|
||||
if(job->LOOPdirection<0 || job->LOOPdirection >= 2)
|
||||
dir=1; /* inverse direction */
|
||||
else
|
||||
dir=0; /* normal direction for 0 and 1 */
|
||||
inst = CKTfndDev(ckt, job->LOOPprobeSrc);
|
||||
|
||||
if (!inst || inst->GENmodPtr->GENmodType < 0) {
|
||||
|
|
@ -163,10 +303,15 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
job->LOOPprobeSrc);
|
||||
return E_NOTFOUND;
|
||||
}
|
||||
printf("LOOPan Vy at %s\n", CKTnodName(ckt,GENnode(inst)[0]));
|
||||
probe_brnum = CKTfndBranch(ckt, job->LOOPprobeSrc);
|
||||
if(0) printf("LOOPan Vy at %s\n", CKTnodName(ckt,GENnode(inst)[dir]));
|
||||
branch_probe = CKTfndBranch(ckt, job->LOOPprobeSrc);
|
||||
phasemargin = 180;
|
||||
gainmargin = strtod("NaN", NULL);
|
||||
phase = gainmargin; /* NaN */
|
||||
pfreq = gainmargin; /* NaN */
|
||||
ugf = gainmargin; /* NaN */
|
||||
ipf = gainmargin; /* NaN */
|
||||
maxgain = 0;
|
||||
|
||||
#ifdef XSPICE
|
||||
/* gtri - add - wbk - 12/19/90 - Add IPC stuff and anal_init and anal_type */
|
||||
|
|
@ -289,7 +434,14 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
|
||||
error = CKTnames(ckt,&numNames,&nameList);
|
||||
if(error) return(error);
|
||||
|
||||
|
||||
#if 0
|
||||
printf("check: branch probe is %s\n", nameList[branch_probe-1]);
|
||||
printf("check: out pos node is %s\n", nameList[job->LOOPoutPos->number-1]);
|
||||
printf("check: out neg node is %s\n", nameList[job->LOOPoutNeg->number-1]);
|
||||
printf("check: in branch node is %s\n", nameList[branch_vin-1]);
|
||||
#endif
|
||||
|
||||
if (ckt->CKTkeepOpInfo) {
|
||||
/* Dump operating point. */
|
||||
error = SPfrontEnd->OUTpBeginPlot (ckt, ckt->CKTcurJob,
|
||||
|
|
@ -303,18 +455,32 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
plot = NULL;
|
||||
}
|
||||
|
||||
SPfrontEnd->IFnewUid (ckt, &freqUid, NULL, "frequency", UID_OTHER, NULL);
|
||||
/* setup for the real loop output plot */
|
||||
numNames = 2; /* T and D by default */
|
||||
if(1) /* yet implemented */
|
||||
if(job->LOOPinSrcGiven)
|
||||
if(job->LOOPoutSrcGiven || (job->LOOPoutPosGiven && job->LOOPoutNegGiven))
|
||||
numNames = LOOP_NCURVES; /* all GFT functions */
|
||||
|
||||
nameList = TMALLOC(IFuid, numNames);
|
||||
for(int i=0;i<numNames;i++)
|
||||
{
|
||||
SPfrontEnd->IFnewUid (ckt, &nameList[i], job->LOOPname, plot_curves[i], UID_OTHER, NULL);
|
||||
}
|
||||
|
||||
SPfrontEnd->IFnewUid (ckt, &freqUid, NULL, "frequency", UID_OTHER, NULL);
|
||||
error = SPfrontEnd->OUTpBeginPlot (ckt, ckt->CKTcurJob,
|
||||
ckt->CKTcurJob->JOBname,
|
||||
freqUid, IF_REAL,
|
||||
numNames, nameList, IF_COMPLEX,
|
||||
&acPlot);
|
||||
tfree(nameList);
|
||||
if(error) return(error);
|
||||
|
||||
if (job->LOOPstepType != LINEAR) {
|
||||
SPfrontEnd->OUTattributes (acPlot, NULL, OUT_SCALE_LOG, NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
freq = job->LOOPstartFreq;
|
||||
|
||||
} else { /* continue previous analysis */
|
||||
|
|
@ -403,7 +569,7 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
|
||||
/************* Main part starts here ********************/
|
||||
size = SMPmatSize(ckt->CKTmatrix);
|
||||
for(int simno=0;simno<2;simno++)
|
||||
for(int simno=0;simno<(numNames>2 ? 3 : 2);simno++)
|
||||
{
|
||||
for (i=0; i<=size; i++)
|
||||
{
|
||||
|
|
@ -422,14 +588,27 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
{
|
||||
case 1:
|
||||
/* AC current src magn=1 from ground to Vx */
|
||||
ckt->CKTrhs[GENnode(inst)[1]] -= 1;
|
||||
ckt->CKTrhs[0] += 1;
|
||||
ckt->CKTrhs[GENnode(inst)[1-dir]] -= 1;
|
||||
if(job->LOOPrefNode)
|
||||
ckt->CKTrhs[job->LOOPrefNode->number] += 1;
|
||||
else
|
||||
ckt->CKTrhs[0] += 1;
|
||||
break;
|
||||
case 0:
|
||||
/* AC voltage src magn=1 accross probe */
|
||||
ckt->CKTrhs[probe_brnum] += 1;
|
||||
ckt->CKTrhs[branch_probe] += 1;
|
||||
break;
|
||||
case 2:
|
||||
/* AC voltage at input source */
|
||||
if(job->LOOPinIV==LOOP_IV_VOLTAGE) {
|
||||
ckt->CKTrhs[branch_vin] += 1;
|
||||
} else if (job->LOOPinIV==LOOP_IV_CURRENT) {
|
||||
ckt->CKTrhs[inPosNode] += 1;
|
||||
ckt->CKTrhs[inNegNode] -= 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
printf("Don't know what to simulate for this index %d\n",simno);
|
||||
return(E_BADPARM);
|
||||
}
|
||||
|
||||
|
|
@ -440,51 +619,79 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
UPDATE_STATS(DOING_AC);
|
||||
return(error);
|
||||
}
|
||||
Vy[simno] = ckt->CKTrhsOld[GENnode(inst)[0]];
|
||||
iVy[simno] = ckt->CKTirhsOld[GENnode(inst)[0]];
|
||||
Iy[simno] = ckt->CKTrhsOld[probe_brnum];
|
||||
iIy[simno] = ckt->CKTirhsOld[probe_brnum];
|
||||
if(0) printf("|Vy| = %g, |Iy|=%g\n", sqrt(Vy[simno]*Vy[simno]+iVy[simno]*iVy[simno]), sqrt(Iy[simno]*Iy[simno]+iIy[simno]*iIy[simno]));
|
||||
if(job->LOOPrefNode) {
|
||||
Vy[simno].real = ckt->CKTrhsOld[GENnode(inst)[dir]] - ckt->CKTrhsOld[job->LOOPrefNode->number];
|
||||
Vy[simno].imag = ckt->CKTirhsOld[GENnode(inst)[dir]] - ckt->CKTirhsOld[job->LOOPrefNode->number];
|
||||
} else {
|
||||
Vy[simno].real = ckt->CKTrhsOld[GENnode(inst)[dir]];
|
||||
Vy[simno].imag = ckt->CKTirhsOld[GENnode(inst)[dir]];
|
||||
}
|
||||
Iy[simno].real = ckt->CKTrhsOld[branch_probe];
|
||||
Iy[simno].imag = ckt->CKTirhsOld[branch_probe];
|
||||
if(job->LOOPoutIV == LOOP_IV_CURRENT) {
|
||||
Uout[simno].real = ckt->CKTrhsOld[branch_iout];
|
||||
Uout[simno].imag = ckt->CKTirhsOld[branch_iout];
|
||||
} else if(job->LOOPoutIV == LOOP_IV_VOLTAGE) {
|
||||
Uout[simno].real = ckt->CKTrhsOld[job->LOOPoutPos->number] -
|
||||
ckt->CKTrhsOld[job->LOOPoutNeg->number];
|
||||
Uout[simno].imag = ckt->CKTirhsOld[job->LOOPoutPos->number] -
|
||||
ckt->CKTirhsOld[job->LOOPoutNeg->number];
|
||||
}
|
||||
|
||||
if(0) printf("|Vy| = %g, |Iy|=%g\n", sqrt(Vy[simno].real*Vy[simno].real+Vy[simno].imag*Vy[simno].imag), sqrt(Iy[simno].real*Iy[simno].real+Iy[simno].imag*Iy[simno].imag));
|
||||
}
|
||||
|
||||
/* V(y)@1*I(Viy)@3-V(y)@3*I(Viy)@1 */
|
||||
{
|
||||
double D, iD, T, iT, ngainsq, nphase;
|
||||
double ngainsq, nphase;
|
||||
|
||||
D = Vy[0]*Iy[1] - iVy[0]*iIy[1] - Vy[1]*Iy[0] + iVy[1]*iIy[0];
|
||||
iD = Vy[0]*iIy[1] + iVy[0]*Iy[1] - Vy[1]*iIy[0] - iVy[1]*Iy[0];
|
||||
D = Vy[0].real*Iy[1].real - Vy[0].imag*Iy[1].imag - Vy[1].real*Iy[0].real + Vy[1].imag*Iy[0].imag;
|
||||
iD = Vy[0].real*Iy[1].imag + Vy[0].imag*Iy[1].real - Vy[1].real*Iy[0].imag - Vy[1].imag*Iy[0].real;
|
||||
/*T = (D-iD*iD/(1-D))/(1-D+iD/(1-D));*/
|
||||
T = (D*(1-D)-iD*iD)/((1-D)*(1-D) + iD*iD);
|
||||
iT = iD/((1-D)*(1-D) + iD*iD);
|
||||
nphase = 180*atan2(-iD,-D)/M_PI;
|
||||
ngainsq = T*T + iT*iT;
|
||||
/* search for phase margin */
|
||||
if((ngainsq-1)*(gainsq-1)<0)
|
||||
{ /* gain cross 1 - interpolate lin/log for more accurate results */
|
||||
double crossphase = (log(gainsq)*nphase -
|
||||
double crossphase, ugflog;
|
||||
crossphase = (log(gainsq)*nphase -
|
||||
log(ngainsq)*phase)/(log(gainsq)-log(ngainsq));
|
||||
if(fabs(crossphase)<phasemargin)
|
||||
phasemargin = fabs(crossphase);
|
||||
ugflog = (log(gainsq)*log(freq) -
|
||||
log(ngainsq)*log(pfreq))/(log(gainsq)-log(ngainsq));
|
||||
if(isnan(ugf))
|
||||
ugf = exp(ugflog);
|
||||
/* idea: binary search/sim + for frequency where gain=1 ? */
|
||||
}
|
||||
|
||||
if(ngainsq>=1)
|
||||
if(fabs(nphase)<phasemargin)
|
||||
phasemargin = fabs(nphase);
|
||||
|
||||
if(T>0 && curTr>0)
|
||||
/* search for gain margin */
|
||||
if(T<0 && curTr<0)
|
||||
if(nphase * phase <= 0)
|
||||
{
|
||||
/* right quandrant and cross phase=0 */
|
||||
double gmsqlog;
|
||||
gmsqlog = (nphase*log(gainsq)-phase*log(ngainsq))/(nphase-phase);
|
||||
gainmargin = 10*gmsqlog;
|
||||
gmsqlog = (phase*log(ngainsq)-nphase*log(gainsq))/(phase-nphase);
|
||||
if(0) printf("phase cross at %g, ngainsq=%g gainsq=%g qmsqlog=%g\n", freq, ngainsq, gainsq, exp(gmsqlog));
|
||||
if(isnan(gainmargin) || (10*gmsqlog*M_LOG10E > gainmargin)) {
|
||||
double ipflog;
|
||||
gainmargin = 10*gmsqlog*M_LOG10E;
|
||||
ipflog = (phase*log(freq)-nphase*log(pfreq))/(phase-nphase);
|
||||
ipf = exp(ipflog);
|
||||
}
|
||||
}
|
||||
|
||||
gainsq=ngainsq;
|
||||
phase=nphase;
|
||||
curTr = T; /* remember real part only */
|
||||
|
||||
if(1) printf("f=%g |T| = %g, |D|=%g, phase=%g\n", freq, sqrt(T*T+iT*iT), sqrt(D*D+iD*iD), 180*atan2(-iD,-D)/M_PI);
|
||||
if(gainsq > maxgain)
|
||||
maxgain = gainsq;
|
||||
if(0) printf("f=%g |T| = %g, |D|=%g, phase=%g\n", freq, sqrt(T*T+iT*iT), sqrt(D*D+iD*iD), 180*atan2(-iD,-D)/M_PI);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -513,28 +720,58 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* error = CKTacDump(ckt,freq,acPlot); */
|
||||
{
|
||||
IFvalue freqData;
|
||||
IFvalue valueData;
|
||||
IFcomplex cval, cA;
|
||||
IFcomplex data[LOOP_NCURVES];
|
||||
freqData.rValue = freq;
|
||||
valueData.v.numValue = numNames;
|
||||
valueData.v.vec.cVec = data;
|
||||
data[LOOP_CURVE_T].real = T;
|
||||
data[LOOP_CURVE_T].imag = iT;
|
||||
data[LOOP_CURVE_D].real = D;
|
||||
data[LOOP_CURVE_D].imag = iD;
|
||||
if(numNames >= LOOP_CURVE_H) {
|
||||
data[LOOP_CURVE_H] = Uout[2];
|
||||
cA.real=0;
|
||||
cA.imag=0;
|
||||
for(int i=0;i<3;i++)
|
||||
cA = caddf(cA, cmulf(Uout[i],csubf(cmulf(Vy[(i+1)%3],Iy[(i+2)%3]),cmulf(Vy[(i+2)%3],Iy[(i+1)%3]))));
|
||||
data[LOOP_CURVE_HINF] = cdivf(cA,data[LOOP_CURVE_D]);
|
||||
data[LOOP_CURVE_DN] = cdivf(Uout[2],cA);
|
||||
data[LOOP_CURVE_TN].real = (data[LOOP_CURVE_DN].real*(1-data[LOOP_CURVE_DN].real)-data[LOOP_CURVE_DN].imag*data[LOOP_CURVE_DN].imag)/((1-data[LOOP_CURVE_DN].real)*(1-data[LOOP_CURVE_DN].real) + data[LOOP_CURVE_DN].imag*data[LOOP_CURVE_DN].imag);
|
||||
data[LOOP_CURVE_TN].imag = data[LOOP_CURVE_DN].imag/((1-data[LOOP_CURVE_DN].real)*(1-data[LOOP_CURVE_DN].real) + data[LOOP_CURVE_DN].imag*data[LOOP_CURVE_DN].imag);
|
||||
data[LOOP_CURVE_D0].real = 1 - data[LOOP_CURVE_D].real;
|
||||
data[LOOP_CURVE_D0].imag = -data[LOOP_CURVE_D].imag;
|
||||
data[LOOP_CURVE_H0] = cdivf(csubf(data[LOOP_CURVE_H],cA),data[LOOP_CURVE_D0]);
|
||||
data[LOOP_CURVE_DP] = cmulf(data[LOOP_CURVE_D],data[LOOP_CURVE_DN]);
|
||||
data[LOOP_CURVE_TP] = cdivf(data[LOOP_CURVE_DP],csubf((IFcomplex) {1,0}, data[LOOP_CURVE_DP]));
|
||||
/* A = Vo.2*(Vy.0*IViy.1-Vy.1*IViy.0)+Vo.0*(Vy.1*IViy.2-Vy.2*IViy.1)+Vo.1*(Vy.2*IViy.0-Vy.0*IViy.2) */
|
||||
/* A = Vo[2]*(Vy[0]*Iy[1]-Vy[1]*Iy[0])+Vo[0]*(Vy[1]*Iy[2]-Vy[2]*Iy[1])+Vo[1]*(Vy[2]*Iy[0]-Vy[0]*Iy[2])*/
|
||||
}
|
||||
#ifdef XSPICE
|
||||
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
|
||||
|
||||
if(g_ipc.enabled)
|
||||
ipc_send_data_prefix(freq);
|
||||
|
||||
error = CKTacDump(ckt,freq,acPlot);
|
||||
|
||||
#endif
|
||||
SPfrontEnd->OUTpData (acPlot, &freqData, &valueData);
|
||||
}
|
||||
#ifdef XSPICE
|
||||
if(g_ipc.enabled)
|
||||
ipc_send_data_suffix();
|
||||
|
||||
/* gtri - modify - wbk - 12/19/90 - Send IPC stuff */
|
||||
#else
|
||||
error = CKTacDump(ckt,freq,acPlot);
|
||||
#endif
|
||||
if (error) {
|
||||
#endif
|
||||
|
||||
if (error) {
|
||||
UPDATE_STATS(DOING_AC);
|
||||
return(error);
|
||||
}
|
||||
|
||||
/* increment frequency */
|
||||
|
||||
pfreq = freq;
|
||||
switch (job->LOOPstepType) {
|
||||
case DECADE:
|
||||
case OCTAVE:
|
||||
|
|
@ -577,9 +814,31 @@ LOOPan(CKTcircuit *ckt, int restart)
|
|||
|
||||
}
|
||||
endsweep:
|
||||
printf("Loop analysis '%s': phase margin is %g [degrees], gain margin is %g [dB]\n", job->LOOPname ? job->LOOPname : "anon", phasemargin, gainmargin);
|
||||
printf("Loop analysis"); if(job->LOOPname) printf(" '%s'",job->LOOPname); printf(": ");
|
||||
if(maxgain>=1.0)
|
||||
printf("phase margin is %g [degrees]\n", phasemargin);
|
||||
else
|
||||
printf("no phase margin detected as loop gain is lower than 1\n");
|
||||
|
||||
printf("Loop analysis"); if(job->LOOPname) printf(" '%s'",job->LOOPname); printf(": ");
|
||||
if(!isnan(gainmargin))
|
||||
printf("gain margin is %g [dB] at f=%g [Hz]\n", gainmargin, ipf);
|
||||
else
|
||||
printf("no gain margin detected as phase do not reach 0 degrees\n");
|
||||
|
||||
printf("Loop analysis"); if(job->LOOPname) printf(" '%s'",job->LOOPname); printf(": ");
|
||||
if(!isnan(ugf))
|
||||
printf("unit gain frequency is %g [Hz]\n", ugf);
|
||||
else
|
||||
printf("loop gain do not cross 1\n");
|
||||
|
||||
if(maxgain<1) {
|
||||
printf("Loop analysis"); if(job->LOOPname) printf(" '%s'",job->LOOPname); printf(": ");
|
||||
printf("hint: try to reverce the loop direction\n");
|
||||
}
|
||||
|
||||
SPfrontEnd->OUTendPlot (acPlot);
|
||||
tfree(nameList);
|
||||
acPlot = NULL;
|
||||
UPDATE_STATS(0);
|
||||
return(0);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ LOOPsetParm(CKTcircuit *ckt, JOB *anal, int which, IFvalue *value)
|
|||
case LOOP_PROBESRC:
|
||||
job->LOOPprobeSrc = value->uValue;
|
||||
break;
|
||||
|
||||
case LOOP_REFNODE:
|
||||
job->LOOPrefNode = value->nValue;
|
||||
job->LOOPrefNodeGiven = 1;
|
||||
break;
|
||||
|
||||
case LOOP_START:
|
||||
if (value->rValue <= 0.0) {
|
||||
|
|
@ -124,6 +129,7 @@ static IFparm LOOPparms[] = {
|
|||
{ "lin", LOOP_LIN, IF_SET|IF_FLAG, "step linearly" },
|
||||
{ "name", LOOP_NAME, IF_SET|IF_STRING, "analysis name" },
|
||||
{ "probe", LOOP_PROBESRC,IF_SET|IF_INSTANCE, "probe inloop" },
|
||||
{ "refnode", LOOP_REFNODE, IF_SET|IF_NODE, "reference node" },
|
||||
{ "portnum", LOOP_PORTNUM, IF_SET|IF_INTEGER, "terminal number for breaking the loop" },
|
||||
{ "portname", LOOP_PORTNAME, IF_SET|IF_STRING, "terminal name for breaking the loop" },
|
||||
{ "dir", LOOP_DIR, IF_SET|IF_INTEGER, "loop direction" },
|
||||
|
|
|
|||
Loading…
Reference in New Issue