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:
Giles Atkinson 2023-07-03 19:19:03 +01:00
parent d3a8634afe
commit bead3e3249
3 changed files with 272 additions and 61 deletions

View File

@ -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);
}
}

View File

@ -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:

View File

@ -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)) {