ftp.nice.ch/pub/next/developer/hardware/dsp/drbub/analog/sspice.c

This is sspice.c in view mode; [Download] [Up]

/***************************************************************************
*		STEVE SPICE			steve punte 10-24-87
*
*	This program is frequency domain only version of spice.  It
*	was developed for the ece-149 class for the simulation of
*	passive and active filters on standard IBM-ATs that normally
*	have insufficient memory for even the reduced version of
*	pspice.  This program is written very flexibbly to request
*	memory from the operating system as it is needed.  Unfortionately
*	the micro-soft C compiler requires that the maximum amount of
*	memory ever needed be stated before hand.  Never the less
*	this is a portable program to any system that compiles C.
*
*****************************************************************************
*
*	This is the header file.  All global variables are defined here.
*	If this file is used in file "main", then the phrase "extern"
*	is stripped of from all declarations.  Otherwise it remains for
*	use in other files.
*
****************************************************************************/

#include	<stdio.h>
#include	<math.h>
#define		TRUE		1
#define		FALSE		0
#define		PIE		3.141592653
#define		STR_LNGTH	20  

#define		MAIN
#ifdef		MAIN		/* Removes extern if used in main.c */
#define		extern	 
#endif
#define		ALLCODE		/* If all code is present, allows proper compliation*/ 
				/* Convenient pointers to dynamically allocated arrays */
#define		addm(I, J)	(addm_mtrx_ptr + I + N * J)
#define		curr(I )	(curr_vect_ptr + I )

extern struct component
	{
	char name[ STR_LNGTH ];		/* Name of component */
	int node_i;			/* One connection of the component */
	char name_i[ STR_LNGTH ];	/* Name of first node */
	int node_j;			/* The second connection of the component */
	char name_j[ STR_LNGTH ];	/* Name of second node */
	int node_k;			/* A third connection, if existing */
	char name_k[ STR_LNGTH ];	/* Name of third node */
	int node_l;			/* A Fourth connection, if existing */
	char name_l[ STR_LNGTH ];	/* Name of fourth node */
	double value;			/* The value of the component */
	struct component *link;		/* Link foward to next structure or NULL */
	};

extern struct component *begin_caps ;	/* Start of capacitor link list */
extern struct component *begin_inds ;	/* Start of inductor link list */
extern struct component *begin_res  ;	/* Start of resistor link list */
extern struct component *begin_vcv  ;	/* Start of VCV link list */
extern struct component *begin_vci  ;	/* Start of VCI link list */
extern struct component *end_caps   ;	/* Last entry of capacitor link list */
extern struct component *end_inds   ;	/* Last entry of inductor link list */
extern struct component *end_res    ;	/* Last entry of resistor link list */
extern struct component *end_vcv    ;	/* Last entry of VCV link list */
extern struct component *end_vci    ;	/* Last entry of VCI link list */

extern struct component VS_source;		/* Special entry for source */
extern struct component OUT_node;		/* Special entry for output node marker */

extern int flag_FS	;
extern int flag_FE	;
extern int flag_FP	;
extern int flag_LOG	;
extern int flag_PHASE	;
extern int flag_RADS	;
extern int flag_PRTCMP  ;
extern int flag_VS    	;
extern int flag_OUT    	;
extern int flag_MTRX   	;

extern double freq_start;		/* Starting Frequency */
extern double freq_end;			/* Ending Frequency */
extern int freq_part;			/* Number of partitions */

extern int N;				/* Number of distinct nodes */
extern struct cmplx_num *addm_mtrx_ptr;	/* Addmittance Matrix Pointer */
extern struct cmplx_num *curr_vect_ptr;	/* Impedance Matrix Pointer */
/* -------------------------------------------------------------------*/
/*************************************************************************
*
*		ENHANCED MATH PACKAGE
*	An extra set of math functions, and in particular
*	macros that perform complex number operations.  Note:
*	all arguenments pass to the complex number macros MUST
*	be pointers to a complex structure, and not an 
*	actual structure.  Also the use of variable names "den"
*	and "cmplx_tmp" will create problems.  These are macros,
*	and not subroutines!
*
**************************************************************************/

struct cmplx_num	{		/* The Complex number structure */
			double real;
			double imag;
			};

#define		abs(X)		( X > 0 ? X : -X )
#define		sqr(X)		( X )*( X )

#define		cset(X, Y, Z)	X->real = Y; X->imag = Z
#define		cmpl(X)		X->imag = - X->imag
#define		cadd(X, Y, Z)	Z->real = X->real + Y->real;\
				Z->imag = X->imag + Y->imag
#define		csub(X, Y, Z)	Z->real = X->real - Y->real;\
				Z->imag = X->imag - Y->imag
#define		cmul(X, Y, Z)	{ \
				struct cmplx_num cmplx_tmp; \
				cmplx_tmp.real = X->real * Y->real - X->imag * Y->imag ; \
				cmplx_tmp.imag = X->real * Y->imag + X->imag * Y->real ; \
				Z->real = cmplx_tmp.real; \
				Z->imag = cmplx_tmp.imag; \
				}
#define		cdiv(X, Y, Z)	{ \
				struct cmplx_num cmplx_tmp; \
				double den; \
				den = sqr( Y->real ) + sqr( Y->imag ); \
				cmplx_tmp.real = X->real * Y->real + X->imag * Y->imag ; \
				cmplx_tmp.imag = X->imag * Y->real - X->real * Y->imag ; \
				Z->imag = cmplx_tmp.imag / den; \
				Z->real = cmplx_tmp.real / den; \
				}
#define		cmag(X)		sqrt( sqr( X->real) + sqr( X->imag))
/* ----------------------------------------------------------------------------*/
/************************************************************************
*
*	Main.c contains the primary loop of the spice simulation, namely
*	the loop that increments frequency.  Main also first calls
*	subroutines to read in all the net list, to check for critical
*	information, and to possibly print the input data.  For each
*	frequency, the addmitance matrix is recalculated, then 
*	inverted.  The proper element is picked off, and printed.
*
*************************************************************************/
#ifndef		ALLCODE
#include	"header.h"
#endif

main()
{
double freq_delta;	/* Frequency increment */
double freq;		/* Current frequency of calculations */
int i;

initialize();
read_input();
if( flag_PRTCMP == TRUE ) print_components();  /* Print all component values */
check_input();


/* Allocate Space for addmitance matrix */
addm_mtrx_ptr = (struct cmplx_num *)malloc( N * N * sizeof( struct cmplx_num) );
if( addm_mtrx_ptr <= NULL ) {
	fprintf( stderr, "Insufficient Malloc memory allocations. Fatal error \n");
	exit(1);
	}
curr_vect_ptr = (struct cmplx_num *)malloc( N * sizeof( struct cmplx_num) );
if( curr_vect_ptr <= NULL ) {
	fprintf( stderr, "Insufficient Malloc memory allocations. Fatal error \n");
	exit(1);
	}

#ifdef	IBM
printf( "1 \n %d , SSPICE \n", freq_part );
#endif

freq_delta = (freq_end - freq_start ) /freq_part;
freq = freq_start ;
for( i = 0; i <= freq_part ; i++ ) {
	double omega;
	fprintf( stderr, "*");

	if( flag_RADS == TRUE ) omega = freq ;
	else omega = 2 * PIE * freq ;

	/* Fill addmittance matrix */
	fill_matrix( omega);
	if( flag_MTRX == TRUE ) print_mtrx( addm_mtrx_ptr );

	/* Take inverse of addmittance matrix */
	inverse_matrix();
	if( flag_MTRX == TRUE ) print_mtrx( addm_mtrx_ptr );

	print_output( freq );

	freq += freq_delta ;
	}

} /* End of main */
/* ----------------------------------------------------------------------- */
/************************************************************************
*
*	This file contains two main subroutines: "fill_matrix" walks
*	through all the component link lists, and makes numeric entries
*	into the addmittance matrx.  This is only done for one frequency
*	at a time.  "inverse_matrx" actually solves for the voltages
*	of all nodes.  The solution ends up in the vector "curr", which
*	originaly stands for the net current at each node.  The net current
*	at each node is zero, with the exception of the source node.  It
*	is assigned an external current of 1.0.
*
***************************************************************************/

#ifndef		ALLCODE
#include	"header.h"
#endif

fill_matrix( omega)
double omega;
{
struct component *x;
struct cmplx_num addmitt;
int i, j;

if( omega == 0.0 ) omega = 0.000001 ; /* To prevent division by zero */

/* Sets entire addmitance matrix to zero */
for( i=0 ; i < N ; i++ ) {
	for( j=0 ; j < N ; j++ ) {
		addm( i, j)->real = 0.0;
		addm( i, j)->imag = 0.0;
		}
	}

/* Enters all resistor addmittances to matrix */
for( x = begin_res; x != NULL ; x = x->link ) { 	/* Walks throught all res. */
	cset( (&addmitt) , (1.0 / x->value), 0.0 );
	add_element( &addmitt, x );
	}

/* Enters all capacitor addmittances to matrix */
for( x = begin_caps; x != NULL ; x = x->link ) { 	/* Walks throught all cap. */
	cset( (&addmitt) , 0.0 , (omega * x->value) );
	add_element( &addmitt, x );
	}

/* Enters all inductor addmittances to matrix */
for( x = begin_inds; x != NULL ; x = x->link ) { 	/* Walks throught all ind. */
	cset( (&addmitt) , 0.0 , ( -1.0 / (omega * x->value)) );
	add_element( &addmitt, x );
	}

/* Enters all Voltage Controlled Current Sources addmittances to matrix */
for( x = begin_vci; x != NULL ; x = x->link ) {
	cset( (&addmitt) ,  x->value, 0.0 );

	if( x->node_k >= 0 ) {  /* Not a ground node */
		if( x->node_i >= 0 ) {
			addm( x->node_k, x->node_i)->real -= addmitt.real;
			addm( x->node_k, x->node_i)->imag -= addmitt.imag;
			}
		if( x->node_j >= 0 ) {
			addm( x->node_k, x->node_j)->real += addmitt.real;
			addm( x->node_k, x->node_j)->imag += addmitt.imag;
			}
		}

	if( x->node_l >= 0 ) {  /* Not a ground node */
		if( x->node_i >= 0 ) {
			addm( x->node_l, x->node_i)->real += addmitt.real;
			addm( x->node_l, x->node_i)->imag += addmitt.imag;
			}
		if( x->node_j >= 0 ) {
			addm( x->node_l, x->node_j)->real -= addmitt.real;
			addm( x->node_l, x->node_j)->imag -= addmitt.imag;
			}
		}
	}

/* Enters all Voltage Controlled Voltage Sources addmittances to matrix */
for( x = begin_vcv; x != NULL ; x = x->link ) {
	cset( (&addmitt) ,  x->value, 0.0 );
	for( i = 0 ; i < N ; i ++) {
		addm( x->node_k, i)->real = 0.0 ;
		addm( x->node_k, i)->imag = 0.0 ;
		}
	cset( addm( x->node_k, x->node_k), 1.0, 0.0 );

	if( x->node_j >= 0 ) {  /* Not a ground node */
		addm( x->node_k, x->node_j)->real = x->value ;
		}
	if( x->node_i >= 0 ) {  /* Not a ground node */
		addm( x->node_k, x->node_i)->real = -( x->value) ;
		}
	}

/* Enters source addmittance to matrix */
for( i = 0 ; i < N ; i ++) {
	addm( (&VS_source)->node_i, i)->real = 0.0 ;
	addm( (&VS_source)->node_i, i)->imag = 0.0 ;
	}
cset( addm( (&VS_source)->node_i, (&VS_source)->node_i), 1.0, 0.0 );


/* Initialize current vertor */
for( i = 0; i < N; i++ ) { cset( curr( i ), 0.0 , 0.0 ); }
cset( curr( (&VS_source)->node_i), 1.0, 0.0 );
} /* End of enter_matrix */

add_element( a, x)
struct cmplx_num *a;
struct component *x;
{

if( (x->node_i >= 0) && (x->node_j >=0) ) {
	addm( x->node_i, x->node_i)->real += a->real;
	addm( x->node_i, x->node_i)->imag += a->imag;

	addm( x->node_j, x->node_j)->real += a->real;
	addm( x->node_j, x->node_j)->imag += a->imag;

	addm( x->node_i, x->node_j)->real -= a->real;
	addm( x->node_i, x->node_j)->imag -= a->imag;

	addm( x->node_j, x->node_i)->real -= a->real;
	addm( x->node_j, x->node_i)->imag -= a->imag;
	}

else if( (x->node_i >= 0) && (x->node_j < 0) ) {
	addm( x->node_i, x->node_i)->real += a->real;
	addm( x->node_i, x->node_i)->imag += a->imag;
	}

else if( (x->node_i < 0) && (x->node_j >= 0) ) {
	addm( x->node_j, x->node_j)->real += a->real;
	addm( x->node_j, x->node_j)->imag += a->imag;
	}

else {
	fprintf( stderr, "Component %s Seems to have no conections to any active nodes.  It is ignored \n", x->name );
	}


}


#define		csubeq(X, Y)	{ \
				X->real -= Y->real; \
				X->imag -= Y->imag; \
				}

/********************************************************************
*  The following routing is an enhanced inversion gaussain elimination
*  inversion routing. Is seeks the largest value in any column, and
*  then performs pivoting and elimination using this entry.  In order to
*  keep track of which columns have already been pivoted, the algorithm
*  checks the sum of the magnitude of the elements along the row to
*  just before the test pivot element.  If this sum is zero, or very
*  close to it, it is assumed that this row has already been pivoited.
**********************************************************************/
inverse_matrix()
{
struct cmplx_num one_x, tmp_x, scale_x;
struct cmplx_num *one, *tmp, *scale;
int i, j, k, ii;
double pre_max;		/* Previous Maximum */
double cur_max;		/* Current Maximum */
double par_row_sum ; 	/* Partial Row Sum.  To detect if this row has already been cleared */

/* Need Temporary space that are pointers */
one = &one_x;
tmp = &tmp_x;
scale = &scale_x;

cset( one, 1.0, 0.0);


/* Scan across all columns */
for( j = 0; j < N; j++) { 

	/* Find Maximun entry in this row. The magnitude of all previous entries in
	   this row is sumed in "par_row_sum".  Only if this sum is zero is it 
	   valid to pivot on this row. */
	pre_max = 0.0 ;
	for( i = 0; i < N; i++ ) {
		par_row_sum = 0.0;
		for( k = 0; k < j ; k++ ) par_row_sum += cmag( addm( i, k) ) ;
		cur_max = cmag( addm( i, j) );

		/* test this pivot point */
		if( ( cur_max > pre_max ) && ( cur_max > (1000000000 * par_row_sum ) )) {
			ii = i;		/* Mark this N as ii */
			pre_max = cur_max ;
			}
		}

	/* Normalize the row chosen above */
	if( cmag( addm( ii, j )) == 0.0 ) {
		fprintf( stderr,"##ERROR##\n, division by zero.\n");
		exit(0);
		}
	cdiv( one , addm( ii, j), scale );
	for( k = j; k < N; k++ ) {
		cmul( addm( ii, k), scale, addm( ii, k) );
		}
	cmul( curr( ii), scale, curr( ii) );


	/* Null out all other entries in this column, except
	   choses entry to zero. */
	for( i = 0; i < N; i++ ) {
		/* Don't do if pivot row, or if semi-pivot element is zero */
		if( ( i != ii ) && ( cmag( addm( i , j ) ) != 0.0) ) {
			cmul( addm( i, j), curr( ii ), tmp );
			csubeq( curr( i ), tmp );
			for( k = N-1; k >= j ; k-- ) {
				cmul( addm( i, j), addm( ii, k), tmp );
				csubeq( addm( i, k), tmp );
				}
			}
		}
	} /* End of Column Scan */
}  /* End of matrix inversion */
/*****************************************************************************
*
*	This file contains subroutines for reading in the net list, processing
*	the node names into node numbers, setting true appropriate flags, and
*	recording misc. infomation, such as starting frequency, etc.
*
*****************************************************************************/

#ifndef		ALLCODE
#include 	"header.h"
#endif
#define		fscanf		my_fscanf

/* Primary input read loop.  Exits when EOF is detected */
read_input()
{
char str[ STR_LNGTH ] ;	/* String buffer for input reading */
FILE *fp_in	= stdin;

/* Loops over all input lines, and enters data into proper variables */
while( fscanf( fp_in, "%s", str) != EOF ) {
	double scan_modifier();
	struct component *add_cmpt_link();

	if( !strcmp( str, "FS") ) {
		fscanf( fp_in, "%f", &freq_start);
		freq_start *= (int)scan_modifier( fp_in );
		flag_FS = TRUE;
		}

	else if( !strcmp( str, "FE") ) {
		fscanf( fp_in, "%f", &freq_end);
		freq_end *= (int)scan_modifier( fp_in );
		flag_FE = TRUE;
		}

	else if( !strcmp( str, "FP") ) {
		fscanf( fp_in, "%d", &freq_part);
		freq_part *= (int)scan_modifier( fp_in );
		flag_FP = TRUE;
		}

	else if( !strcmp( str, "PHASE") ) {
		flag_PHASE = TRUE;
		scan_modifier( fp_in );
		}

	else if( !strcmp( str, "LOG") ) {
		flag_LOG = TRUE;
		scan_modifier( fp_in );
		}

	else if( !strcmp( str, "MTRX") ) {
		flag_MTRX = TRUE;
		scan_modifier( fp_in );
		}

	else if( !strcmp( str, "RADS") ) {
		flag_RADS = TRUE;
		scan_modifier( fp_in );
		}

	else if( !strcmp( str, "PRTCMP") ) {
		flag_PRTCMP = TRUE;
		scan_modifier( fp_in );
		}

	else if( !strcmp( str, "OUT") ) {
		flag_OUT = TRUE;
		fscanf( fp_in, "%s", OUT_node.name_i );
		OUT_node.node_i = node_numb( OUT_node.name_i);
		scan_modifier( fp_in );
		}

	else if( *str == 'L' ) {
		add_cmpt_link( &begin_inds, &end_inds );
		strcpy( end_inds->name, str);
		fscanf( fp_in, "%s", end_inds->name_i );
		end_inds->node_i = node_numb( end_inds->name_i);
		fscanf( fp_in, "%s", end_inds->name_j );
		end_inds->node_j = node_numb( end_inds->name_j);
		fscanf( fp_in, "%f", &end_inds->value );
		end_inds->value *= scan_modifier( fp_in );
		}

	else if( *str == 'C' ) {
		add_cmpt_link( &begin_caps, &end_caps );
		strcpy( end_caps->name, str);
		fscanf( fp_in, "%s", end_caps->name_i );
		end_caps->node_i = node_numb( end_caps->name_i);
		fscanf( fp_in, "%s", end_caps->name_j );
		end_caps->node_j = node_numb( end_caps->name_j);
		fscanf( fp_in, "%f", &end_caps->value );
		end_caps->value *= scan_modifier( fp_in );
		}

	else if( *str == 'R' ) {
		add_cmpt_link( &begin_res, &end_res );
		strcpy( end_res->name, str);
		fscanf( fp_in, "%s", end_res->name_i );
		end_res->node_i = node_numb( end_res->name_i);
		fscanf( fp_in, "%s", end_res->name_j );
		end_res->node_j = node_numb( end_res->name_j);
		fscanf( fp_in, "%f", &end_res->value );
		end_res->value *= scan_modifier( fp_in );
		}

	else if( !strcmp( str, "VCI") ) {
		add_cmpt_link( &begin_vci, &end_vci );
		strcpy( end_vci->name, str);
		fscanf( fp_in, "%s", end_vci->name_i );
		end_vci->node_i = node_numb( end_vci->name_i);
		fscanf( fp_in, "%s", end_vci->name_j );
		end_vci->node_j = node_numb( end_vci->name_j);
		fscanf( fp_in, "%s", end_vci->name_k );
		end_vci->node_k = node_numb( end_vci->name_k);
		fscanf( fp_in, "%s", end_vci->name_l );
		end_vci->node_l = node_numb( end_vci->name_l);
		fscanf( fp_in, "%f", &end_vci->value );
		end_vci->value *= scan_modifier( fp_in );
		}

	else if( !strcmp( str, "VCV") ) {
		add_cmpt_link( &begin_vcv, &end_vcv );
		strcpy( end_vcv->name, str);
		fscanf( fp_in, "%s", end_vcv->name_i );
		end_vcv->node_i = node_numb( end_vcv->name_i);
		fscanf( fp_in, "%s", end_vcv->name_j );
		end_vcv->node_j = node_numb( end_vcv->name_j);
		fscanf( fp_in, "%s", end_vcv->name_k );
		end_vcv->node_k = node_numb( end_vcv->name_k);
		fscanf( fp_in, "%f", &end_vcv->value );
		end_vcv->value *= scan_modifier( fp_in );
		}

	else if( !strcmp( str, "VS") ) {
		flag_VS = TRUE;
		fscanf( fp_in, "%s", VS_source.name_i );
		VS_source.node_i = node_numb( VS_source.name_i);
		scan_modifier( fp_in );
		}


	else {
		fprintf( stderr, "Syntax Error: Unrecognized input string \"%s\". String ignored. \n", str );
		}
	} /* End of while */
} /* End of read routine */



		

/*  Assigns a number to any ascii node name.  Number are chosen in
*   sequential order beginning with zero.  If the name passed to
*   this routing already exits, then the node number assigned
*   to this name previously is returned. Gnd and several variations
*   are special reserved names which always have the value -1
*   associated with them.
*/
node_numb( str )
char *str;
{
struct name_list { 		/* Link List structure to keep node names */
	char name[ STR_LNGTH ];
	int node_number;
	struct name_list *link;
	};

static struct name_list *bgn_nm_lst = NULL;
static struct name_list *end_nm_lst = NULL;
struct name_list *new_link, *x;

if( !strcmp( str, "GND") || !strcmp( str, "gnd") || !strcmp( str, "Gnd") ) return( -1 );
for( x = bgn_nm_lst; x != NULL; x = x->link )
	{
	if( !strcmp( x->name, str)) return( x->node_number );
	}

new_link = (struct name_list *)malloc( sizeof (struct name_list));

new_link->link = NULL;
if( bgn_nm_lst == NULL ) bgn_nm_lst = new_link; /* IF first link, start chain */
else end_nm_lst->link = new_link; 		/* link head */
end_nm_lst = new_link; 				/* Update end pointer */

strcpy( new_link->name, str);
new_link->node_number = N++;
return( new_link->node_number );
}


#undef		fscanf
/* A lousy fucking patch for micro-soft C.  It doesn't
*  seem to read floating point numbers properly.      
*/
my_fscanf( fp, str, reg)
FILE *fp;
char *str;
union {
	int x;
	double y;
	char t;
	} *reg;
	
{
double atof();
if( !strcmp( str, "%f") ) {
	char temp[40];
	fscanf( fp, "%s", temp);
	reg->y = atof( temp);
	}
else fscanf( fp, str, reg);
}
#define		fscanf		my_fscanf
/* ------------------------------------------------------------------------ */
/********************************************************************
*
*	This file contains all routines which print anything to
*	the user.
*
**********************************************************************/

#ifndef		ALLCODE
#include 	"header.h"
#endif

#define 	FLG_STR(X)	if( X == TRUE ) strcpy( str, "TRUE" ); \
						else strcpy( str, "FALSE")

/* Prints components and all other important information */
print_components()
{
FILE *fp_out	= stdout;
char str[ STR_LNGTH ] ;	/* String buffer for input reading */
struct component *x;	/* a pointer to current component structure */

FLG_STR(flag_FS);
fprintf( fp_out,"+Starting frequency flag is set %s \n", str);
FLG_STR(flag_FE);
fprintf( fp_out,"+Ending frequency flag is set %s \n", str);
FLG_STR(flag_FP);
fprintf( fp_out,"+Number of Points  flag is set %s \n", str);
FLG_STR(flag_RADS);
fprintf( fp_out,"+Radian flag is set %s \n", str);
FLG_STR(flag_LOG);
fprintf( fp_out,"+Logarithmitic flag is set %s \n", str);
FLG_STR(flag_VS);
fprintf( fp_out,"+Source designation flag is set %s \n", str);
FLG_STR(flag_OUT);
fprintf( fp_out,"+Output designation flag is set %s \n", str);
fprintf( fp_out,"\n");

if( flag_FS == TRUE ) fprintf( fp_out,"+Starting Frequency = %f \n", freq_start);
if( flag_FE == TRUE ) fprintf( fp_out,"+Ending Frequency = %f \n", freq_end);
if( flag_FP == TRUE ) fprintf( fp_out,"+Number of partitions is = %d \n", freq_part);

fprintf( fp_out, "\n   Component   I Node   J Node   K Node   L Node     Value  \n");

for( x = begin_res; x != NULL ; x = x->link )
	fprintf( fp_out, "+ %10s %8s %8s                       %10e Ohms \n", x->name, x->name_i, x->name_j, x->value);

for( x = begin_inds; x != NULL ; x = x->link ) 
	fprintf( fp_out, "+ %10s %8s %8s                       %10e Heneries \n", x->name, x->name_i, x->name_j, x->value);

for( x = begin_caps; x != NULL ; x = x->link )
	fprintf( fp_out, "+ %10s %8s %8s                       %10e Farads \n", x->name, x->name_i, x->name_j, x->value);

for( x = begin_vcv; x != NULL ; x = x->link )
	fprintf( fp_out, "+ %10s %8s %8s %8s              %10e Av \n", x->name, x->name_i, x->name_j, x->name_k , x->value);

for( x = begin_vci; x != NULL ; x = x->link )
	fprintf( fp_out, "+ %10s %8s %8s %8s %8s     %10e Mohs \n", x->name, x->name_i, x->name_j, x->name_k , x->name_l , x->value);

fprintf( fp_out, "+         VS %8s \n", VS_source.name_i);
fprintf( fp_out, "+        OUT %8s \n", OUT_node.name_i);

} /* End of print routine */




/*  Prints out all elements of a complex matrix 
*   Primarily to be used for checking and debugging 
*/
print_mtrx( mtrx )
struct cmplx_num *mtrx;
{
FILE *fp_out	= stdout ;
int i, j;

fprintf( fp_out,"------------------------------------------------------------ \n");
for( i=0; i<N ; i++ ) {
	for( j=0; j<N ; j++ ) fprintf( fp_out, "%6.2f  ", (mtrx + i + N*j)->real);
	fprintf( fp_out, "   %6.2f\n", curr( i )->real);
	for( j=0; j<N ; j++ ) fprintf( fp_out, "%6.2f  ", (mtrx + i + N*j)->imag);
	fprintf( fp_out, "   %6.2f\n", curr( i )->imag);
	fprintf( fp_out, "\n");
	}
fprintf( fp_out,"------------------------------------------------------------ \n");
}
		


/* Prints the output transfer function */
print_output( omega)
double omega;
{
double magnitude, phase;
int i, ii, out;
double pre_max;

out = OUT_node.node_i ;
pre_max = 0.0 ;
for( i =0 ; i < N ; i++ ) {
	if( cmag( addm( i, out )) > pre_max ) {
		ii = i;
		pre_max = cmag( addm( i, out ));
		}
	}
	

phase = atan2( curr( ii )->imag, curr( ii )->real ); 
magnitude = cmag( curr( ii ) );
if( magnitude == 0.0 ) magnitude = 1000000000.0;

#ifdef	IBM
if( flag_LOG == TRUE ) printf( "%f , %f \n", omega, ( -20 * log10( magnitude ) ) );
else if( flag_PHASE == TRUE ) printf( "%f , %f \n", omega, ( 180 * phase / PIE) ); 
else printf( "%f , %f \n", omega, magnitude );

#else
if( flag_LOG == TRUE ) printf( "%f %f \n", omega, ( -20 * log10( magnitude ) ) );
else if( flag_PHASE == TRUE ) printf( "%f %f \n", omega, ( 180 * phase / PIE ) ); 
else printf( "%f %f \n", omega, magnitude );
#endif
}
/* ----------------------------------------------------------------------------- */
/****************************************************************************
*	
*	General miscellaneous subroutines that I didn't want to
*	be cluttering up the more improtant files.
*
*****************************************************************************/

#ifndef		ALLCODE
#include	"header.h"
#endif


/* Looks for some type of value modifie, such as kilo (x1000)
*  or micro (x0.000001).  Then will be expecting a colon as end
*  of line marker.  Returns a double persion number for
*  scaling purposes.	
*/

double scan_modifier( input )
FILE *input;
{
double scale = 1.0;
char string[ STR_LNGTH ];

while( (fscanf( input, "%s", string) != EOF) && (*string != ';' ) ) {

	switch( *string) {

	case 'K':
		scale = 1000;
		break;

	case 'M':
		scale = 1000000;
		break;

	case 'G':
		scale = 1000000000;
		break;

	case 'm':
		scale = 0.001;
		break;

	case 'u':
		scale = 0.000001;
		break;

	case 'n':
		scale = 0.000000001;
		break;

	case 'p':
		scale = 0.000000000001;
		break;

	case 'E':
		{
		int K;
		fscanf( input, "%d", &K);
		if( K > 30 ) K = 30;
		if( K < -30 ) K = -30;
		while( K-- > 0 ) scale *= 10;
		while( K++ < 0 ) scale *= 0.1;
		}
		break;

	default:
		fprintf( stderr, "Unrecognized string \"%s\" \n", string);
		fprintf( stderr, "Metric modifying unit expected. \n" );
		fprintf( stderr, "String Ignored. \n");
		

		}
	}
return( scale );
}


/* Add a Component structor to the Link.  A component structor is
*  added to the link list.  The end_ptr_add arguement is the ADDRESS of a
*  pointer which points to the last structure in the list.  In the
*  case of a zero list, "pointer_address" is the ADDRESS of the
*  variable "begin_list"
*/

struct component *add_cmpt_link( bgn_ptr_add, end_ptr_add )
struct component **end_ptr_add;
struct component **bgn_ptr_add;
{
struct component *new_link;

if( ( new_link = (struct component *)malloc( sizeof ( struct component ) ) ) == NULL )  {
	fprintf( stderr, "Operating system fault Insufficient memory resources. \n"); 
	fprintf( stderr, "Fatal Error. \n");
	exit(1);
	}

new_link->link = NULL;					/* Link tail */
if( *bgn_ptr_add == NULL ) *bgn_ptr_add = new_link;	/* IF first, Start Chain */
else (*end_ptr_add)->link = new_link;			/* link head */
*end_ptr_add = new_link;				/* Update end pointer */ 
return( new_link );
}



/*
*  Initializes global variables only.
*  This cannot be nicely done in header.h due to
*  its dual nature use with a makefile
*/

initialize()
{
begin_caps = NULL;	/* Start of capacitor link list */
begin_inds = NULL;	/* Start of inductor link list */
begin_res  = NULL;	/* Start of resistor link list */
begin_vcv  = NULL;	/* Start of VCV link list */
begin_vci  = NULL;	/* Start of VCI link list */
end_caps   = NULL;	/* Last entry of capacitor link list */
end_inds   = NULL;	/* Last entry of inductor link list */
end_res    = NULL;	/* Last entry of resistor link list */
end_vcv    = NULL;	/* Last entry of VCV link list */
end_vci    = NULL;	/* Last entry of VCI link list */

flag_FS		=	FALSE;
flag_FE		=	FALSE;
flag_FP		=	FALSE;
flag_LOG	=	FALSE;
flag_PHASE	=	FALSE;
flag_RADS	=	FALSE;
flag_PRTCMP 	=	FALSE;
flag_VS 	=	FALSE;
flag_OUT 	=	FALSE;
flag_MTRX 	=	FALSE;

N		= 	0;
}


/* Checks if essential input informaiton is present */
check_input()
{

if( flag_VS == FALSE ) {
	fprintf( stderr, "No source node has been specified! \n" );
	fprintf( stderr, "Fatal Error. \n");
	exit(1);
	}

if( flag_OUT == FALSE ) {
	fprintf( stderr, "No output node has been specified! \n" );
	fprintf( stderr, "Fatal Error. \n");
	exit(1);
	}

if( flag_FS == FALSE ) {
	fprintf( stderr, "Starting frequency unspecified! \n" );
	fprintf( stderr, "Fatal Error. \n");
	exit(1);
	}

if( flag_FE == FALSE ) {
	fprintf( stderr, "Ending frequency unspecified! \n" );
	fprintf( stderr, "Fatal Error. \n");
	exit(1);
	}

if( flag_FP == FALSE ) {
	fprintf( stderr, "Number of frequency partitions unspecified! \n" );
	fprintf( stderr, "Default of 20 will be used. \n");
	freq_part = 20;
	}
}
/* ------------------------------------------------------------------------- */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.