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
84821a4cf5
commit
4cc0ac846d
|
|
@ -1,24 +1,32 @@
|
||||||
/* Transfer function block for AC simulation, based on s_xfer code model. */
|
/* Transfer function block for AC simulation, based on s_xfer code model. */
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#define PI 3.141592653589793238462643383279502884197
|
#define PI 3.141592653589793238462643383279502884197
|
||||||
|
#define ALLOC 1024
|
||||||
|
|
||||||
/* How the table information is stored internally. */
|
/* How the table information is stored internally. */
|
||||||
|
|
||||||
struct data_pt {
|
struct data {
|
||||||
double f; /* Frequency, radians/sec. */
|
int size; /* Table size. */
|
||||||
Mif_Complex_t s; /* The S-parameter. */
|
double *f; /* Frequency table, radians/sec. */
|
||||||
|
Mif_Complex_t *s; /* The S-parameter table. */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void cleanup(ARGS, Mif_Callback_Reason_t reason)
|
static void cleanup(ARGS, Mif_Callback_Reason_t reason)
|
||||||
{
|
{
|
||||||
struct data_pt *table;
|
struct data *table;
|
||||||
|
|
||||||
switch (reason) {
|
switch (reason) {
|
||||||
case MIF_CB_DESTROY:
|
case MIF_CB_DESTROY:
|
||||||
table = (struct data_pt *)STATIC_VAR(table);
|
table = (struct data *)STATIC_VAR(table);
|
||||||
if (table) {
|
if (table) {
|
||||||
|
free(table->f);
|
||||||
|
free(table->s);
|
||||||
free(table);
|
free(table);
|
||||||
STATIC_VAR(table) = NULL;
|
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. */
|
void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */
|
||||||
{
|
{
|
||||||
struct data_pt *table;
|
struct data *table;
|
||||||
Mif_Complex_t ac_gain;
|
Mif_Complex_t ac_gain;
|
||||||
double factor;
|
double factor;
|
||||||
int span, size, i;
|
int span, size, i;
|
||||||
|
|
||||||
span = PARAM(span);
|
|
||||||
if (INIT) {
|
if (INIT) {
|
||||||
Mif_Boolean_t ri, db, rad;
|
Mif_Boolean_t ri, db, rad;
|
||||||
int offset, bad = 0, j;
|
double *file_data;
|
||||||
|
int offset, bad = 0, j;
|
||||||
/* Validate table. */
|
|
||||||
|
|
||||||
offset = PARAM(offset);
|
offset = PARAM(offset);
|
||||||
size = PARAM_SIZE(table);
|
span = PARAM(span);
|
||||||
bad = size % span;
|
if (offset < 1 || span < offset + 2) {
|
||||||
if (!bad) {
|
cm_message_send("Error: impossible span/offset.");
|
||||||
for (i = 0; i < size - span; i += span) {
|
return;
|
||||||
if (PARAM(table[i]) < 0 ||
|
}
|
||||||
PARAM(table[i + span]) < PARAM(table[i])) {
|
|
||||||
bad = 1;
|
/* Table or File? */
|
||||||
break;
|
|
||||||
|
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) {
|
||||||
if (bad) {
|
cm_message_send("Error: badly formed table.");
|
||||||
cm_message_send("Warning: badly formed table.");
|
return;
|
||||||
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. */
|
/* Allocate the internal table. */
|
||||||
|
|
||||||
size /= span;
|
table = (struct data *)malloc(sizeof(struct data));
|
||||||
table = (struct data_pt *)calloc(size, sizeof(struct data_pt));
|
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;
|
STATIC_VAR(table) = table;
|
||||||
CALLBACK = cleanup;
|
CALLBACK = cleanup;
|
||||||
|
|
||||||
/* Fill it. */
|
/* Fill it. */
|
||||||
|
|
||||||
ri = PARAM(r_i);
|
|
||||||
db = PARAM(db);
|
|
||||||
rad = PARAM(rad);
|
|
||||||
for (i = 0, j = 0; i < size; i++, j += span) {
|
for (i = 0, j = 0; i < size; i++, j += span) {
|
||||||
table[i].f = PARAM(table[j]) * 2.0 * PI;
|
double f, a, b;
|
||||||
if (ri) {
|
|
||||||
table[i].s.real = PARAM(table[j + offset]);
|
|
||||||
table[i].s.imag = PARAM(table[j + offset + 1]);
|
|
||||||
} else {
|
|
||||||
double phase, mag;
|
|
||||||
|
|
||||||
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)
|
if (db)
|
||||||
mag = pow(10, mag / 20);
|
a = pow(10, a / 20);
|
||||||
phase = PARAM(table[j + offset + 1]);
|
|
||||||
if (!rad)
|
if (!rad)
|
||||||
phase *= 2 * PI / 360;
|
b *= 2 * PI / 360;
|
||||||
table[i].s.real = mag * cos(phase);
|
table->s[i].real = a * cos(b);
|
||||||
table[i].s.imag = mag * sin(phase);
|
table->s[i].imag = a * sin(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (file_data)
|
||||||
|
free(file_data);
|
||||||
}
|
}
|
||||||
|
table = (struct data *)STATIC_VAR(table);
|
||||||
table = (struct data_pt *)STATIC_VAR(table);
|
|
||||||
if (!table)
|
if (!table)
|
||||||
return;
|
return;
|
||||||
if (ANALYSIS == MIF_AC) {
|
if (ANALYSIS == MIF_AC) {
|
||||||
double rv;
|
double rv;
|
||||||
|
|
||||||
size = PARAM_SIZE(table) / span;
|
size = table->size;
|
||||||
rv = RAD_FREQ;
|
rv = RAD_FREQ;
|
||||||
if (rv <= table[0].f) {
|
if (rv <= table->f[0]) {
|
||||||
ac_gain = table[0].s;
|
ac_gain = table->s[0];
|
||||||
} else if (rv >= table[size - 1].f) {
|
} else if (rv >= table->f[size - 1]) {
|
||||||
ac_gain = table[size - 1].s;
|
ac_gain = table->s[size - 1];
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < size; i++) {
|
for (i = 0; i < size; i++) {
|
||||||
if (table[i].f > rv)
|
if (table->f[i] > rv)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Linear interpolation. */
|
/* Linear interpolation. */
|
||||||
|
|
||||||
factor = (rv - table[i - 1].f) / (table[i].f - table[i - 1].f);
|
factor = (rv - table->f[i - 1]) / (table->f[i] - table->f[i - 1]);
|
||||||
ac_gain.real = table[i - 1].s.real +
|
ac_gain.real = table->s[i - 1].real +
|
||||||
factor * (table[i].s.real - table[i - 1].s.real);
|
factor * (table->s[i].real - table->s[i - 1].real);
|
||||||
ac_gain.imag = table[i - 1].s.imag +
|
ac_gain.imag = table->s[i - 1].imag +
|
||||||
factor * (table[i].s.imag - table[i - 1].s.imag);
|
factor * (table->s[i].imag - table->s[i - 1].imag);
|
||||||
}
|
}
|
||||||
AC_GAIN(out, in) = ac_gain;
|
AC_GAIN(out, in) = ac_gain;
|
||||||
} else { /* DC, transient ... */
|
} else { /* DC, transient ... */
|
||||||
|
|
@ -124,7 +329,6 @@ void cm_xfer(ARGS) /* structure holding parms, inputs, outputs, etc. */
|
||||||
"transient analysis.");
|
"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
|
Parameter_Name: table
|
||||||
Description: "PWL table: frequency/magnitude/phase"
|
Description: "PWL table: frequency/magnitude/phase"
|
||||||
Data_Type: real
|
Data_Type: real
|
||||||
Default_Value: -
|
Default_Value: 0
|
||||||
Limits: -
|
Limits: -
|
||||||
Vector: yes
|
Vector: yes
|
||||||
Vector_Bounds: [3 -]
|
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:
|
PARAMETER_TABLE:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -579,10 +579,6 @@ MIF_INP2A (
|
||||||
tfree(emessage);
|
tfree(emessage);
|
||||||
gc_end();
|
gc_end();
|
||||||
return;
|
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)) {
|
if((! mdfast->param[i]->is_null) && (param_info->is_array)) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue