program CirAnl; { Performs Nodal Circuit Analysis for Freq Resp }

{             Jeff Crawford         December 10, 1984              }
{                                                                  }
{                          Version  1.0                            }
{                                                                  }
{                           REFERENCES                             }
{                                                                  }
{   [1]  "Unify Two-Port Calculations", D. J. Daruvala, Electronic }
{        Design, January 4, & January 18, 1974 - Two Articles      }
{   [2]  "High-Frequency Amplifiers, 2nd Edition", Ralph S. Carson,}
{        John Wiley & Sons, 1982                                   }


type     matrix    = array[1..25,1..25] of real;
         vector    = array[1..100] of real;
         XYData    = record
                       XX,YY: real;
                     end;
         stat      = string[1];


var      L,Cap         : matrix; { Inductance, Capacitance Terms Sqmodel }
         Fyi,Fyr       : matrix; { Admittance Matrices; Real, Imag       }
         F,F_low,F_high: real;   { Frequency Information                 }
         F_inc         : real;   { Increment of Frequency                }
         Nodes         : integer;{ Number of Nodes in Circuit; Passed from}
                                 { Sqmodel Main Program                  }
         omega         : real;   { Radian Frequency                      }
         N             : integer;{ Parameter in Node Reduction Routine   }
                                 { N Starts at Nodes and is Reduced by 1 }
                                 { Until Arriving at N=2 for a Two-Port  }
         No_turns      : integer;{ # Turns in Inductor - Passed from Main}
         A,B           : matrix; { Equiv of Fyr,Fyi Matrices; Used in    }
                                 { Matrix Reduction Routine to a 2 x 2   }
         i,j           : integer;{ Used in Looping                       }
         Freq,Mag      : vector; { Arrays to Hold Information of Z v.s. F}
         Ans,Ans1,Ans2 : stat;   { Controls Function Routing          }
         Num           : integer; { Number of Data Points Store on Disk  }


Procedure Write_File( X,Y: vector);

var      j              : integer;
         write_file     : file of XYData;
         read_file      : file of XYData;
         file_rec       : XYData;
         file_name      : string[10];
         file_size      : integer;
         OK             : Boolean;
         Ans1,Ans2      : string[1]; { Controls Program Routing      }

label 1,2;

begin
2:   clrscr;
     gotoXY(10,10);
     write('Write Data to What File  ? ');
     readln(file_name);
     assign(write_file,file_name);
     {$I-} Reset(write_file) {$I+};
     OK:= (IOresult = 0);     { Determines if File Already Exists }
     if OK then
         begin
         clrscr;
         gotoXY(10,10);
         write('File Already Exists . . . Overwrite ?  Y / N ');
         readln(Ans2);
         Ans2:= upcase(Ans2);
         if Ans2 <> 'Y' then goto 1;
         end;
    assign(write_file,file_name);
    rewrite(write_file);
    with file_rec do
         begin
         for j:= 1 to Num do   { Writes Data to Disk File }
              begin
              XX:= X[j];
              YY:= X[j];
              write(write_file,file_rec);
              end;
         end;
    flush(write_file);
    close(write_file);
1:  if Ans2 = 'N' then
         begin
         clrscr;
         gotoXY(10,10);
         write('Write Data to Different File   Y / N ?  ');
         readln(Ans1);
         Ans1:= upcase(Ans1);
         if Ans1 = 'Y' then goto 2;
         end;
end;


Procedure Swap(var X1,Y1: real); { Used to Swap Rows & Columns to   }
                             { Change Input and/or Output Terminals }
                             { for Calculation; Declared "var" so   }
                             { Switch is Made in Major Matrices     }

    var   XX: real;

    begin
         XX:= X1;
         X1:= Y1;
         Y1:= XX;
    end;



Procedure Save_C(V: real; i,j: integer);  { Saves Capacitors }

    var  temp: real;

    begin
         temp:= omega*V;
         Fyi[i,i]:= Fyi[i,i] + temp;
         Fyi[j,j]:= Fyi[j,j] + temp;
         Fyi[i,j]:= Fyi[i,j] - temp;
         Fyi[j,i]:= Fyi[j,i] - temp;
    end;

Procedure Save_L(V: real; i,j: integer);

    var  temp: real;

    begin
         temp:= -1.0/(omega*V);
         Fyi[i,i]:= Fyi[i,i] + temp;
         Fyi[j,j]:= Fyi[j,j] + temp;
         Fyi[i,j]:= Fyi[i,j] - temp;
         Fyi[j,i]:= Fyi[j,i] - temp;
    end;



Procedure Fill_Caps(var N: integer);
    var  i : integer;

    begin
         for i:= 2 to N-1 do
              begin
                   Save_C(cap[i-1,i-1],i,0);
              end;
         for i:= 2 to N-2 do
              begin
                   Save_C(cap[i-1,i], i, i+1);
              end;
    end;


Procedure Fill_Inductors(var N: integer);

    var  i              : integer;
         Ind_val        : real;

    begin
         Save_L(0.5*L[1,1],1,2);
         Save_L(0.5*L[N-2,N-2], N-1,N);
         for i:= 2 to N-2 do
              begin
                   Ind_val:= 1/(0.5*(L[i-1,i-1] + L[i,i])) + 1.0/L[i-1,i];
                                { i,j are Coupled Turn Designators }
                   Save_L(1.0/Ind_val,i,i+1);
              end;
         for i:= 2 to N-1 do
              begin
                   for j:= i+2 to N-1 do
                        begin
                             Save_L(L[i-1,j-1], i,j);
                        end;
               end;
    end;


Procedure Reduce_Mat(In_node,Out_node:integer; N:integer; var A,B:matrix);

{ Reduces Beginning N*N Floating Admittance Matrix to an Equivalent Two - }
{ Port Representation by Proper Matrix Reduction Techniques Discussed in  }
{ the References Sited.  After the Necessary Swapping to Place Input Node }
{ in Columns/Row 1 and Output Node in 2, Reference Node in 3, The Starting}
{ Matrix is Reduced Down to a Two-Port Representation.                    }

var      Den      : real; { Magnitude of Yrr                              }
         r        : integer; { Row & Column Being Reduced                 }
         i,j      : integer;
         LL       : integer; { Largest Diagonal Element Row - Better      }
         XX1      : real; { Real Part of Common Term Subtracted From      }
                          { Yps in Reduction Process                      }
         YY1      : real; { Imag Part of Common Term Subtracted From      }
                          { Yps in Reduction Process                      }
         S1,S     : real; { Tests for Largest Diagonal Element; Done to   }
                          { Retain as Much Accuracy as Possible in the    }
                          { Reduction Process                             }


    begin
         if (In_node <> 1) or (Out_node <> 2) then   { Swap In & Out }
         begin
              for j:= 1 to N do
                   begin
                   Swap(A[1,j], A[In_node,j]);
                   Swap(B[1,j], B[In_node,j]);
                   Swap(A[2,j], A[Out_node,j]);
                   Swap(B[2,j], B[Out_node,j]);
                   end;
              for i:= 1 to N do
                   begin
                   Swap(A[i,1], A[i,In_node]);
                   Swap(B[i,1], B[i,In_node]);
                   Swap(A[i,2], A[i,Out_node]);
                   Swap(B[i,2], B[i,Out_node]);
                   end;
         end;
    while N > 2 do { Finds Largest Diagonal Element in Rows Remaining     }
                   { to be Reduced to Enhance Accuracy and Performs the   }
                   { Necessary Swapping                                   }
         begin
         LL:= 3;
         S:= abs( A[LL,LL] ) + abs( B[LL,LL] );
         if N > 3 then
              begin
              for i:= 4 to N do
                   begin
                   S1:= abs( A[i,i] ) + abs( B[i,i] );
                   if S1 > S then
                        begin
                        S:= S1;
                        LL:= i;
                        end;
                   end;
              end;
         if LL <> N then
              begin
              for j:= 1 to N do
                   begin
                   Swap( A[LL,j],A[N,j]);
                   Swap( B[LL,j],B[N,j]);
                   end;
              for i:= 1 to N do
                   begin
                   Swap( A[i,LL], A[i,N]);
                   Swap( B[i,LL],B[i,N]);
                   end;
              end;
         Den:= sqr(A[N,N]) + sqr(B[N,N]); { Beginning of Main Reduction }
         r:= N;
         N:= N - 1;
         for i:= 1 to N do
              begin
              if (A[i,r] <> 0) or (B[i,r] <> 0) then
                   begin
                   XX1:= (A[i,r]*A[r,r] + B[r,r]*B[i,r])/Den;
                   YY1:= (A[r,r]*B[i,r] - A[i,r]*B[r,r])/Den;
                   for j:= 1 to N do
                        begin
                        if (A[r,j]<> 0) or (B[r,j]<> 0) then
                             begin
                             A[i,j]:= A[i,j] - A[r,j]*XX1 + B[r,j]*YY1;
                             B[i,j]:= B[i,j] - A[r,j]*YY1 - B[r,j]*XX1;
                             end;
                        end;
                   end;
              end;
         end;
end;

Procedure Out_put(Ans2: stat; var FYr,FYi:matrix );

begin
    write('  ',F:6:3,'  ',Fyr[1,1]:6:4,'  ',Fyi[1,1]:6:4,'  ',Fyr[1,2]:6:4);
    write('  ',Fyi[1,2]:6:4,'  ',Fyr[2,1]:6:4,'  ',Fyi[2,1]:6:4);
    writeln('  ',Fyr[2,2]:6:4,'  ',Fyi[2,2]:6:4);
    if Ans2 = 'Y' then
    begin
    write(LST,'  ',F:6:3,'  ',Fyr[1,1]:6:4,'  ',Fyi[1,1]:6:4,'  ',Fyr[1,2]:6:4);
    write(LST,'  ',Fyi[1,2]:6:4,'  ',Fyr[2,1]:6:4,'  ',Fyi[2,1]:6:4);
    writeln(LST,'  ',Fyr[2,2]:6:4,'  ',Fyi[2,2]:6:4);
    end;
end;


{ //////////////////////////////////////////////////////////////////// }
{ //////////////////////         MAIN              /////////////////// }
{ //////////////////////        BLOCK              /////////////////// }
{ //////////////////////////////////////////////////////////////////// }

begin
No_turns:= 4;
L[1,2]:= 3.08175E-11;
L[1,3]:= 2.54812E-11;
L[1,4]:= 2.27640E-11;
L[2,3]:= 4.78563E-11;
L[2,4]:= 3.85507E-11;
L[3,4]:= 6.57417E-11;
L[1,1]:= 7.7183E-11;
L[2,2]:= 1.2684E-10;
L[3,3]:= 1.8074E-10;
L[4,4]:= 2.378E-10;
cap[1,2]:= 6.21531E-15;
cap[2,3]:= 8.56065E-15;
cap[3,4]:= 1.0906E-14;
cap[2,0]:= 1.41149E-15;
cap[3,0]:= 1.85961E-15;
cap[1,1]:= 4.02368E-15;
cap[4,4]:= 9.6381E-15;
for i:= 1 to 25 do
    begin
    for j:= 1 to 25 do
         begin
         Fyr[i,j]:= 0.0;
         Fyi[i,j]:= 0.0;
         A[i,j]:= 0.0;
         B[i,j]:= 0.0;
         end;
    end;
Ans2:= 'N';
clrscr;
gotoXY(10,10);
write('Enter Lower, Upper, & Freq-Increment,  GHz  ');
readln(F_low, F_high, F_inc);
F:= F_low;
writeln;
write('         Is a Hard-Copy Desired  Y / N ');
readln(Ans2);
Ans2:= upcase(Ans2);
Nodes:= No_turns + 2;
Num:= 0;
clrscr;
gotoXY(1,2);
write('                         Two - Port Admittance Parameters ');
writeln;
write('  Freq           Y11             Y12    ');
writeln('         Y21             Y22');
write('  GHz        Real    Imag     Real Imag  ');
writeln('    Real  Imag      Real  Imag');
writeln;
if Ans2 = 'Y' then
    begin
    writeln(LST,'                   Two - Port Admittance Parameters ');
    writeln(LST);
    write(LST,'  Freq           Y11              Y12    ');
    writeln(LST,'         Y21             Y22');
    write(LST,'  GHz        Real    Imag      Real Imag  ');
    writeln(LST,'    Real  Imag      Real  Imag');
    writeln(LST);
    end;
while F<= F_high do
    begin
    Num:= Num + 1;
    Omega:= F*2*Pi*1.0E9;
    N:= Nodes;
    for i:= 1 to N do
         begin
         for j:= 1 to N do
              begin
              Fyr[i,j]:= 0.0;
              Fyi[i,j]:= 0.0;
              end;
         end;
    Fill_Caps(N);
    Fill_inductors(N);
    Reduce_Mat(1,Nodes,N,Fyr,Fyi);
    Freq[Num]:= F;
    Mag[Num]:= -1.0/Fyi[1,1];
    Out_put(Ans2,Fyr,Fyi);
    F:= F + F_inc;
    end;
    write('                                              ');
    writeln('          Any Key to Continue ');
    repeat
        delay(100);
    until keypressed;
    clrscr;
    gotoXY(10,10);
    write('Store Data to Disk ?  Y/N ');
    readln(Ans);
    Ans:= upcase(Ans);
    if Ans = 'Y' then Write_file(Freq,Mag);
end.


