Add an option for parameters to the xfer code model to be provided
in a Touchstone-format file instead of as a model parameter. The change to mif_inp2.c allows the "table" parameter to be omitted.
This commit is contained in:
parent
d3a8634afe
commit
bead3e3249
|
|
@ -1,24 +1,32 @@
|
|||
/* Transfer function block for AC simulation, based on s_xfer code model. */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#define PI 3.141592653589793238462643383279502884197
|
||||
#define ALLOC 1024
|
||||
|
||||
/* How the table information is stored internally. */
|
||||
|
||||
struct data_pt {
|
||||
double f; /* Frequency, radians/sec. */
|
||||
Mif_Complex_t s; /* The S-parameter. */
|
||||
struct data {
|
||||
int size; /* Table size. */
|
||||
double *f; /* Frequency table, radians/sec. */
|
||||
Mif_Complex_t *s; /* The S-parameter table. */
|
||||
};
|
||||
|
||||
static void cleanup(ARGS, Mif_Callback_Reason_t reason)
|
||||
{
|
||||
struct data_pt *table;
|
||||
struct data *table;
|
||||
|
||||
switch (reason) {
|
||||
case MIF_CB_DESTROY:
|
||||
table = (struct data_pt *)STATIC_VAR(table);
|
||||
table = (struct data *)STATIC_VAR(table);
|
||||
if (table) {
|
||||
free(table->f);
|
||||
free(table->s);
|
||||
free(table);
|
||||
STATIC_VAR(table) = NULL;
|
||||
}
|
||||
|
|
@ -26,94 +34,291 @@ static void cleanup(ARGS, Mif_Callback_Reason_t reason)
|
|||
}
|
||||
}
|
||||
|
||||
static double *read_file(const char *fn, int span, int offset,
|
||||
int *size_p, Mif_Boolean_t *ri, Mif_Boolean_t *db)
|
||||
{
|
||||
FILE *fp;
|
||||
double *file_data;
|
||||
double mult;
|
||||
int i, j, line, size, count, skip, want;
|
||||
char *cp, *word;
|
||||
double vals[9];
|
||||
char buff[1024];
|
||||
|
||||
fp = fopen(fn, "r");
|
||||
if (fp == NULL) {
|
||||
cm_message_printf("Can not open file %s: %s", fn, strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Find and examine the option line. */
|
||||
|
||||
line = 0;
|
||||
while (fgets(buff, sizeof buff, fp)) {
|
||||
++line;
|
||||
if (buff[0] == '#')
|
||||
break;
|
||||
}
|
||||
if (buff[0] != '#') {
|
||||
cm_message_printf("No option line found in file %s", fn);
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
mult = 1.0e9; // Default to GHz
|
||||
for (cp = buff + 1; *cp; ++cp) {
|
||||
while (isspace(*cp))
|
||||
++cp;
|
||||
if (*cp) {
|
||||
word = cp;
|
||||
while (*cp && !isspace(*cp))
|
||||
++cp;
|
||||
*cp++ = '\0';
|
||||
|
||||
if (!strcmp(word, "MHz"))
|
||||
mult = 1.0e6;
|
||||
else if (!strcmp(word, "KHz"))
|
||||
mult = 1.0e3;
|
||||
else if (!strcmp(word, "Hz"))
|
||||
mult = 1.0;
|
||||
else if (!strcmp(word, "DB"))
|
||||
*db = MIF_TRUE;
|
||||
else if (!strcmp(word, "RI"))
|
||||
*ri = MIF_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read the data: at most 9 values per line. */
|
||||
|
||||
size = ALLOC;
|
||||
file_data = malloc(size * sizeof(double));
|
||||
if (!file_data)
|
||||
goto bad;
|
||||
want = skip = i = 0;
|
||||
|
||||
while (fgets(buff, sizeof buff, fp)) {
|
||||
++line;
|
||||
count = sscanf(buff, "%lg%lg%lg%lg%lg%lg%lg%lg%lg",
|
||||
vals, vals + 1, vals + 2, vals + 3, vals + 4,
|
||||
vals + 5, vals + 6, vals + 7, vals + 8);
|
||||
if (!count)
|
||||
continue;
|
||||
if (span == 9 && count == 5) {
|
||||
/* Special case: noise data in 2-port file. */
|
||||
|
||||
cm_message_printf("Ignoring noise parameters in file %s", fn);
|
||||
break;
|
||||
}
|
||||
if (skip) {
|
||||
if (count > skip) {
|
||||
j = skip;
|
||||
skip = 0;
|
||||
} else {
|
||||
skip -= count;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
j = 0;
|
||||
}
|
||||
|
||||
/* Check allocation. */
|
||||
|
||||
if (i + 3 > size) {
|
||||
size += ALLOC;
|
||||
file_data = realloc(file_data, size * sizeof(double));
|
||||
if (!file_data)
|
||||
goto bad;
|
||||
}
|
||||
|
||||
while (j < count) {
|
||||
/* Store value. */
|
||||
|
||||
file_data[i++] = vals[j];
|
||||
switch (want) {
|
||||
case 0:
|
||||
/* Stored a frequency value. */
|
||||
|
||||
if (j != 0)
|
||||
cm_message_printf("Warning: frequency not at start "
|
||||
"of line %d",
|
||||
line);
|
||||
want = 2;
|
||||
j += offset;
|
||||
if (j >= count)
|
||||
skip = j - count;
|
||||
break;
|
||||
case 1:
|
||||
want = 0;
|
||||
j += span - offset - 1;
|
||||
if (j >= count)
|
||||
skip = j - count;
|
||||
break;
|
||||
case 2:
|
||||
j++;
|
||||
want = 1;
|
||||
skip = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (want || skip)
|
||||
cm_message_send("Warning: unexpected end of file data.");
|
||||
*size_p = i / 3;
|
||||
bad:
|
||||
fclose(fp);
|
||||
return file_data;
|
||||
}
|
||||
|
||||
void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */
|
||||
{
|
||||
struct data_pt *table;
|
||||
struct data *table;
|
||||
Mif_Complex_t ac_gain;
|
||||
double factor;
|
||||
int span, size, i;
|
||||
|
||||
span = PARAM(span);
|
||||
if (INIT) {
|
||||
Mif_Boolean_t ri, db, rad;
|
||||
int offset, bad = 0, j;
|
||||
|
||||
/* Validate table. */
|
||||
Mif_Boolean_t ri, db, rad;
|
||||
double *file_data;
|
||||
int offset, bad = 0, j;
|
||||
|
||||
offset = PARAM(offset);
|
||||
size = PARAM_SIZE(table);
|
||||
bad = size % span;
|
||||
if (!bad) {
|
||||
for (i = 0; i < size - span; i += span) {
|
||||
if (PARAM(table[i]) < 0 ||
|
||||
PARAM(table[i + span]) < PARAM(table[i])) {
|
||||
bad = 1;
|
||||
break;
|
||||
span = PARAM(span);
|
||||
if (offset < 1 || span < offset + 2) {
|
||||
cm_message_send("Error: impossible span/offset.");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Table or File? */
|
||||
|
||||
if (PARAM(file) == NULL) {
|
||||
/* Data given by "table" parameter. */
|
||||
|
||||
file_data = NULL;
|
||||
size = PARAM_SIZE(table);
|
||||
bad = size % span || size == 0;
|
||||
|
||||
/* Validate table. */
|
||||
|
||||
if (!bad) {
|
||||
for (i = 0; i < size - span; i += span) {
|
||||
if (PARAM(table[i]) < 0 ||
|
||||
PARAM(table[i + span]) < PARAM(table[i])) {
|
||||
bad = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bad) {
|
||||
cm_message_send("Warning: badly formed table.");
|
||||
return;
|
||||
if (bad) {
|
||||
cm_message_send("Error: badly formed table.");
|
||||
return;
|
||||
}
|
||||
size /= span; /* Size now in entries. */
|
||||
ri = PARAM(r_i);
|
||||
db = PARAM(db);
|
||||
rad = PARAM(rad);
|
||||
} else {
|
||||
/* Get data from a Touchstone file. */
|
||||
|
||||
if (!PARAM_NULL(table)) {
|
||||
cm_message_send("Error: both file and table "
|
||||
"parameters given.");
|
||||
return;
|
||||
} else {
|
||||
db = MIF_FALSE;
|
||||
ri = MIF_FALSE;
|
||||
file_data = read_file(PARAM(file), span, offset,
|
||||
&size, &ri, &db);
|
||||
if (file_data == NULL)
|
||||
return;
|
||||
span = 3;
|
||||
rad = MIF_FALSE;
|
||||
|
||||
/* Explicit parameters override file. */
|
||||
|
||||
if (!PARAM_NULL(r_i))
|
||||
ri = PARAM(r_i);
|
||||
if (!PARAM_NULL(db))
|
||||
db = PARAM(db);
|
||||
if (!PARAM_NULL(rad))
|
||||
rad = PARAM(rad);
|
||||
}
|
||||
}
|
||||
|
||||
/* Allocate the internal table. */
|
||||
|
||||
size /= span;
|
||||
table = (struct data_pt *)calloc(size, sizeof(struct data_pt));
|
||||
table = (struct data *)malloc(sizeof(struct data));
|
||||
if (!table)
|
||||
return;
|
||||
table->size = size;
|
||||
table->f = (double*)malloc(size * sizeof (double));
|
||||
if (!table->f) {
|
||||
free(table);
|
||||
return;
|
||||
}
|
||||
table->s = ( Mif_Complex_t *)malloc(size * sizeof (Mif_Complex_t));
|
||||
if (!table->s) {
|
||||
free(table->f);
|
||||
free(table);
|
||||
return;
|
||||
}
|
||||
STATIC_VAR(table) = table;
|
||||
CALLBACK = cleanup;
|
||||
|
||||
/* Fill it. */
|
||||
|
||||
ri = PARAM(r_i);
|
||||
db = PARAM(db);
|
||||
rad = PARAM(rad);
|
||||
for (i = 0, j = 0; i < size; i++, j += span) {
|
||||
table[i].f = PARAM(table[j]) * 2.0 * PI;
|
||||
if (ri) {
|
||||
table[i].s.real = PARAM(table[j + offset]);
|
||||
table[i].s.imag = PARAM(table[j + offset + 1]);
|
||||
} else {
|
||||
double phase, mag;
|
||||
double f, a, b;
|
||||
|
||||
mag = PARAM(table[j + offset]);
|
||||
if (file_data) {
|
||||
f = file_data[j];
|
||||
a = file_data[j + 1];
|
||||
b = file_data[j + 2];
|
||||
} else {
|
||||
f = PARAM(table[j]);
|
||||
a = PARAM(table[j + offset]);
|
||||
b = PARAM(table[j + offset + 1]);
|
||||
}
|
||||
table->f[i] = f * 2.0 * PI;
|
||||
if (ri) {
|
||||
table->s[i].real = a;
|
||||
table->s[i].imag = b;
|
||||
} else {
|
||||
if (db)
|
||||
mag = pow(10, mag / 20);
|
||||
phase = PARAM(table[j + offset + 1]);
|
||||
a = pow(10, a / 20);
|
||||
if (!rad)
|
||||
phase *= 2 * PI / 360;
|
||||
table[i].s.real = mag * cos(phase);
|
||||
table[i].s.imag = mag * sin(phase);
|
||||
b *= 2 * PI / 360;
|
||||
table->s[i].real = a * cos(b);
|
||||
table->s[i].imag = a * sin(b);
|
||||
}
|
||||
}
|
||||
if (file_data)
|
||||
free(file_data);
|
||||
}
|
||||
|
||||
table = (struct data_pt *)STATIC_VAR(table);
|
||||
table = (struct data *)STATIC_VAR(table);
|
||||
if (!table)
|
||||
return;
|
||||
if (ANALYSIS == MIF_AC) {
|
||||
double rv;
|
||||
|
||||
size = PARAM_SIZE(table) / span;
|
||||
size = table->size;
|
||||
rv = RAD_FREQ;
|
||||
if (rv <= table[0].f) {
|
||||
ac_gain = table[0].s;
|
||||
} else if (rv >= table[size - 1].f) {
|
||||
ac_gain = table[size - 1].s;
|
||||
if (rv <= table->f[0]) {
|
||||
ac_gain = table->s[0];
|
||||
} else if (rv >= table->f[size - 1]) {
|
||||
ac_gain = table->s[size - 1];
|
||||
} else {
|
||||
for (i = 0; i < size; i++) {
|
||||
if (table[i].f > rv)
|
||||
if (table->f[i] > rv)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Linear interpolation. */
|
||||
|
||||
factor = (rv - table[i - 1].f) / (table[i].f - table[i - 1].f);
|
||||
ac_gain.real = table[i - 1].s.real +
|
||||
factor * (table[i].s.real - table[i - 1].s.real);
|
||||
ac_gain.imag = table[i - 1].s.imag +
|
||||
factor * (table[i].s.imag - table[i - 1].s.imag);
|
||||
factor = (rv - table->f[i - 1]) / (table->f[i] - table->f[i - 1]);
|
||||
ac_gain.real = table->s[i - 1].real +
|
||||
factor * (table->s[i].real - table->s[i - 1].real);
|
||||
ac_gain.imag = table->s[i - 1].imag +
|
||||
factor * (table->s[i].imag - table->s[i - 1].imag);
|
||||
}
|
||||
AC_GAIN(out, in) = ac_gain;
|
||||
} else { /* DC, transient ... */
|
||||
|
|
@ -124,7 +329,6 @@ void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */
|
|||
"transient analysis.");
|
||||
}
|
||||
}
|
||||
OUTPUT(out) = table[0].s.real * INPUT(in);
|
||||
OUTPUT(out) = table->s[0].real * INPUT(in);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,11 +23,22 @@ PARAMETER_TABLE:
|
|||
Parameter_Name: table
|
||||
Description: "PWL table: frequency/magnitude/phase"
|
||||
Data_Type: real
|
||||
Default_Value: -
|
||||
Default_Value: 0
|
||||
Limits: -
|
||||
Vector: yes
|
||||
Vector_Bounds: [3 -]
|
||||
Null_Allowed: no
|
||||
Null_Allowed: yes
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
Parameter_Name: file
|
||||
Description: "File in Touchstone format"
|
||||
Data_Type: string
|
||||
Default_Value: -
|
||||
Limits: -
|
||||
Vector: no
|
||||
Vector_Bounds: -
|
||||
Null_Allowed: yes
|
||||
|
||||
PARAMETER_TABLE:
|
||||
|
||||
|
|
|
|||
|
|
@ -579,10 +579,6 @@ MIF_INP2A (
|
|||
tfree(emessage);
|
||||
gc_end();
|
||||
return;
|
||||
} else if((param_info->is_array) && (! param_info->has_conn_ref)) {
|
||||
LITERR("Defaulted array parameter must have associated array connection");
|
||||
gc_end();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if((! mdfast->param[i]->is_null) && (param_info->is_array)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue