committed by
Holger Vogt
9 changed files with 289 additions and 6 deletions
-
2examples/noise/resdiv.cir
-
1src/include/ngspice/osdiitf.h
-
1src/osdi/Makefile.am
-
3src/osdi/osdi.h
-
2src/osdi/osdidefs.h
-
1src/osdi/osdiext.h
-
1src/osdi/osdiinit.c
-
252src/osdi/osdinoise.c
-
32src/osdi/osdiregistry.c
@ -0,0 +1,252 @@ |
|||||
|
/* |
||||
|
* This file is part of the OSDI component of NGSPICE. |
||||
|
* Copyright© 2023 Pascal Kuthe. |
||||
|
* |
||||
|
* This Source Code Form is subject to the terms of the Mozilla Public |
||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this |
||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. |
||||
|
* |
||||
|
* Author: Pascal Kuthe <pascal.kuthe@semimod.de> |
||||
|
*/ |
||||
|
|
||||
|
#include "ngspice/cktdefs.h" |
||||
|
#include "ngspice/iferrmsg.h" |
||||
|
#include "ngspice/ngspice.h" |
||||
|
#include "ngspice/noisedef.h" |
||||
|
#include "ngspice/suffix.h" |
||||
|
|
||||
|
#include "osdi.h" |
||||
|
#include "osdidefs.h" |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
#include <stdio.h> |
||||
|
#include <stdlib.h> |
||||
|
#include <string.h> |
||||
|
#include <sys/types.h> |
||||
|
|
||||
|
#ifdef RFSPICE |
||||
|
extern CMat *eyem; |
||||
|
extern CMat *zref; |
||||
|
extern CMat *gn; |
||||
|
extern CMat *gninv; |
||||
|
extern CMat *vNoise; |
||||
|
extern CMat *iNoise; |
||||
|
#include "../maths/dense/denseinlines.h" |
||||
|
#endif |
||||
|
|
||||
|
static double *noise_dens = NULL; |
||||
|
static double *noise_dens_ln = NULL; |
||||
|
static uint32_t noise_dense_len = 0; |
||||
|
|
||||
|
#define nVar(i, j) noise_vals[i * descr->num_noise_src + j] |
||||
|
/* |
||||
|
* HICUMnoise (mode, operation, firstModel, ckt, data, OnDens) |
||||
|
* |
||||
|
* This routine names and evaluates all of the noise sources |
||||
|
* associated with HICUM's. It starts with the model *firstModel and |
||||
|
* traverses all of its insts. It then proceeds to any other models |
||||
|
* on the linked list. The total output noise density generated by |
||||
|
* all of the HICUM's is summed with the variable "OnDens". |
||||
|
*/ |
||||
|
|
||||
|
int OSDInoise(int mode, int operation, GENmodel *inModel, CKTcircuit *ckt, |
||||
|
Ndata *data, double *OnDens) { |
||||
|
GENmodel *gen_model; |
||||
|
GENinstance *gen_inst; |
||||
|
uint32_t i, node1, node2; |
||||
|
double realVal, imagVal, gain, tempOnoise, tempInoise, totalNoise, |
||||
|
totalNoiseLn, inoise; |
||||
|
OsdiNoiseSource src; |
||||
|
uint32_t *node_mapping; |
||||
|
double *noise_vals; |
||||
|
NOISEAN *job = (NOISEAN *)ckt->CKTcurJob; |
||||
|
|
||||
|
OsdiRegistryEntry *entry = osdi_reg_entry_model(inModel); |
||||
|
const OsdiDescriptor *descr = entry->descriptor; |
||||
|
|
||||
|
if (descr->num_noise_src == 0) { |
||||
|
return OK; |
||||
|
} |
||||
|
|
||||
|
if (noise_dense_len < descr->num_noise_src) { |
||||
|
noise_dens = realloc(noise_dens, descr->num_noise_src * sizeof(double)); |
||||
|
noise_dens_ln = |
||||
|
realloc(noise_dens_ln, descr->num_noise_src * sizeof(double)); |
||||
|
} |
||||
|
|
||||
|
for (gen_model = inModel; gen_model; gen_model = gen_model->GENnextModel) { |
||||
|
void *model = osdi_model_data(gen_model); |
||||
|
|
||||
|
for (gen_inst = gen_model->GENinstances; gen_inst; |
||||
|
gen_inst = gen_inst->GENnextInstance) { |
||||
|
void *inst = osdi_instance_data(entry, gen_inst); |
||||
|
totalNoise = 0.0; |
||||
|
noise_vals = osdi_noise_data(entry, gen_inst); |
||||
|
|
||||
|
switch (operation) { |
||||
|
|
||||
|
case N_OPEN: |
||||
|
/* see if we have to to produce a summary report */ |
||||
|
/* if so, name all the noise generators */ |
||||
|
if (job->NStpsSm != 0) { |
||||
|
switch (mode) { |
||||
|
|
||||
|
case N_DENS: |
||||
|
for (i = 0; i < descr->num_noise_src; i++) { |
||||
|
NOISE_ADD_OUTVAR(ckt, data, "onoise_%s_%s", gen_inst->GENname, |
||||
|
descr->noise_sources[i].name); |
||||
|
} |
||||
|
// TOTAL noise |
||||
|
NOISE_ADD_OUTVAR(ckt, data, "onoise_%s%s", gen_inst->GENname, ""); |
||||
|
break; |
||||
|
|
||||
|
case INT_NOIZ: |
||||
|
for (i = 0; i < descr->num_noise_src; i++) { |
||||
|
NOISE_ADD_OUTVAR(ckt, data, "onoise_total_%s_%s", |
||||
|
gen_inst->GENname, descr->noise_sources[i].name); |
||||
|
NOISE_ADD_OUTVAR(ckt, data, "inoise_total_%s_%s", |
||||
|
gen_inst->GENname, descr->noise_sources[i].name); |
||||
|
} |
||||
|
// TOTAL noise |
||||
|
NOISE_ADD_OUTVAR(ckt, data, "onoise_total_%s%s", gen_inst->GENname, |
||||
|
" "); |
||||
|
NOISE_ADD_OUTVAR(ckt, data, "inoise_total_%s%s", gen_inst->GENname, |
||||
|
" "); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
break; |
||||
|
case N_CALC: |
||||
|
switch (mode) { |
||||
|
|
||||
|
case N_DENS: |
||||
|
descr->load_noise(inst, model, data->freq, noise_dens); |
||||
|
node_mapping = |
||||
|
(uint32_t *)(((char *)inst) + descr->node_mapping_offset); |
||||
|
for (i = 0; i < descr->num_noise_src; i++) { |
||||
|
src = descr->noise_sources[i]; |
||||
|
node1 = node_mapping[src.nodes.node_1]; |
||||
|
if (src.nodes.node_2 == UINT32_MAX) { |
||||
|
node2 = 0; |
||||
|
} else { |
||||
|
node2 = node_mapping[src.nodes.node_2]; |
||||
|
}; |
||||
|
#ifdef RFSPICE |
||||
|
if (ckt->CKTcurrentAnalysis & DOING_SP) { |
||||
|
inoise = sqrt(noise_dens[i]); |
||||
|
// Calculate input equivalent noise current source (we have port |
||||
|
// impedance attached) |
||||
|
for (int s = 0; s < ckt->CKTportCount; s++) |
||||
|
vNoise->d[0][s] = |
||||
|
cmultdo(csubco(ckt->CKTadjointRHS->d[s][node1], |
||||
|
ckt->CKTadjointRHS->d[s][node2]), |
||||
|
inoise); |
||||
|
|
||||
|
for (int d = 0; d < ckt->CKTportCount; d++) { |
||||
|
cplx in; |
||||
|
double yport = 1.0 / zref->d[d][d].re; |
||||
|
|
||||
|
in.re = vNoise->d[0][d].re * yport; |
||||
|
in.im = vNoise->d[0][d].im * yport; |
||||
|
|
||||
|
for (int s = 0; s < ckt->CKTportCount; s++) |
||||
|
caddc(&in, in, |
||||
|
cmultco(ckt->CKTYmat->d[d][s], vNoise->d[0][s])); |
||||
|
|
||||
|
iNoise->d[0][d] = in; |
||||
|
} |
||||
|
|
||||
|
for (int d = 0; d < ckt->CKTportCount; d++) |
||||
|
for (int s = 0; s < ckt->CKTportCount; s++) |
||||
|
ckt->CKTNoiseCYmat->d[d][s] = |
||||
|
caddco(ckt->CKTNoiseCYmat->d[d][s], |
||||
|
cmultco(iNoise->d[0][d], conju(iNoise->d[0][s]))); |
||||
|
|
||||
|
continue; |
||||
|
} |
||||
|
|
||||
|
#endif |
||||
|
realVal = ckt->CKTrhs[node1] - ckt->CKTrhs[node2]; |
||||
|
imagVal = ckt->CKTirhs[node1] - ckt->CKTirhs[node2]; |
||||
|
gain = (realVal * realVal) + (imagVal * imagVal); |
||||
|
noise_dens[i] *= gain; |
||||
|
noise_dens_ln[i] = log(MAX(noise_dens[i], N_MINLOG)); |
||||
|
totalNoise += noise_dens[i]; |
||||
|
} |
||||
|
#ifdef RFSPICE |
||||
|
if (ckt->CKTcurrentAnalysis & DOING_SP) { |
||||
|
continue; |
||||
|
; |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
*OnDens += totalNoise; |
||||
|
totalNoiseLn = log(MAX(totalNoise, N_MINLOG)); |
||||
|
if (data->delFreq == 0.0) { |
||||
|
/* if we haven't done any previous integration, we need to */ |
||||
|
/* initialize our "history" variables */ |
||||
|
|
||||
|
for (i = 0; i < descr->num_noise_src; i++) { |
||||
|
nVar(LNLSTDENS, i) = noise_dens_ln[i]; |
||||
|
} |
||||
|
|
||||
|
/* clear out our integration variables if it's the first pass */ |
||||
|
|
||||
|
if (data->freq == job->NstartFreq) { |
||||
|
for (i = 0; i < descr->num_noise_src; i++) { |
||||
|
nVar(OUTNOIZ, i) = 0.0; |
||||
|
nVar(INNOIZ, i) = 0.0; |
||||
|
} |
||||
|
nVar(OUTNOIZ, descr->num_noise_src) = 0.0; |
||||
|
nVar(INNOIZ, descr->num_noise_src) = 0.0; |
||||
|
} |
||||
|
} else { /* data->delFreq != 0.0 (we have to integrate) */ |
||||
|
|
||||
|
/* In order to get the best curve fit, we have to integrate each |
||||
|
* component separately */ |
||||
|
|
||||
|
for (i = 0; i < descr->num_noise_src; i++) { |
||||
|
tempOnoise = Nintegrate(noise_dens[i], noise_dens_ln[i], |
||||
|
nVar(LNLSTDENS, i), data); |
||||
|
tempInoise = |
||||
|
Nintegrate(noise_dens[i] * data->GainSqInv, |
||||
|
noise_dens_ln[i] + data->lnGainInv, |
||||
|
nVar(LNLSTDENS, i) + data->lnGainInv, data); |
||||
|
nVar(LNLSTDENS, i) = noise_dens_ln[i]; |
||||
|
data->outNoiz += tempOnoise; |
||||
|
data->inNoise += tempInoise; |
||||
|
if (job->NStpsSm != 0) { |
||||
|
nVar(OUTNOIZ, i) += tempOnoise; |
||||
|
nVar(INNOIZ, i) += tempInoise; |
||||
|
nVar(OUTNOIZ, descr->num_noise_src) += tempOnoise; |
||||
|
nVar(INNOIZ, descr->num_noise_src) += tempInoise; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if (data->prtSummary) { |
||||
|
for (i = 0; i < descr->num_noise_src; |
||||
|
i++) { /* print a summary report */ |
||||
|
data->outpVector[data->outNumber++] = noise_dens[i]; |
||||
|
} |
||||
|
data->outpVector[data->outNumber++] = totalNoise; |
||||
|
} |
||||
|
break; |
||||
|
case INT_NOIZ: /* already calculated, just output */ |
||||
|
if (job->NStpsSm != 0) { |
||||
|
for (i = 0; i <= descr->num_noise_src; i++) { |
||||
|
data->outpVector[data->outNumber++] = nVar(OUTNOIZ, i); |
||||
|
data->outpVector[data->outNumber++] = nVar(INNOIZ, i); |
||||
|
} |
||||
|
} /* if */ |
||||
|
break; |
||||
|
} /* switch (mode) */ |
||||
|
break; |
||||
|
|
||||
|
case N_CLOSE: |
||||
|
return (OK); /* do nothing, the main calling routine will close */ |
||||
|
break; /* the plots */ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return (OK); |
||||
|
} |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue