MODULE CircuitLib
! ===================================================================
! Circuit analysis module
! Tips & Techniques 1
! Paul Castonguay
!
! True BASIC, Inc.
! November, 1991
! ===================================================================
!
! SUBROUTINE LIST:
!
!      Generate_Log_Chart
!      Generate_Smith_Chart
!         X_Arc .......................... PRIVATE
!         Draw_X_Arc ..................... PRIVATE
!      SmithPlot
!      PrintZ
!      Scale_Log_chart
!      Scale_Smith_Chart
!
! FUNCTION LIST:
!
!      Graphic$
!


    SHARE Zo

    PRIVATE X_Arc, Draw_X_Arc




! ===================================================================
! SUBROUTINE: Generate_Log_Chart
! ===================================================================
!
! PURPOSE:  To draw multi-cycle logarithmic charts for 
!           characterization of circuits over a range of frequencies.
!
! PARAMETERS:
!
!    INPUT: Low_Freq ..... Frequency at left edge of chart
!
!           High_Freq .... Frequency at right edge of chart
!
!           Low_Linear ... Scale at bottom of chart
!
!           High_Linear .. Scale at top of chart
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES: NONE
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: NONE
!
! POSTCONDITIONS:
! 
!     1 - Chart drawn to screen
!
!     2 - Viewport scaled to INPUT values.
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!    1 - Check for negative frequencies.
!
!    2 - Verify that frequency limits are multiples of 10.
!
!    3 - Verify that low frequency value is less than high.
!
!    4 - a) Calculate number of cycles in chart
!
!        b) Calculate division between horizontal lines
!
!        b) Scale viewport
!
!    5 - Draw horizontal lines
!
!    6 - Draw vertical lines
!
! DIAGRAMS: NONE
!
! ===================================================================
SUB Generate_Log_Chart(Low_Freq, High_Freq, Low_Linear, High_Linear)

   ! ******************************
   ! Check for negative frequencies
   ! ******************************
   IF Low_Freq < 0 or High_Freq < 0 THEN 
      WINDOW #0
      CLEAR
      PRINT "Error in Generate_Log_Chart()"
      PRINT "Negative frequency."
      STOP
   END IF

   ! *****************************************
   ! Make sure frequencies are multiples of 10
   ! *****************************************
   IF MOD(Low_Freq, 10) <> 0 THEN
      WINDOW #0 
      CLEAR
      PRINT "Error in Generate_Log_Chart()"
      PRINT "Low_Freq not integral multiple of 10."
      STOP
   END IF

   IF MOD(High_Freq, 10) <> 0 THEN
      WINDOW #0 
      CLEAR
      PRINT "Error in Generate_Log_Chart()"
      PRINT "High_Freq not integral multiple of 10."
      STOP
   END IF

   ! *******************************
   ! Check for backwards frequencies
   ! *******************************
   IF Low_Freq >= High_Freq THEN 
      WINDOW #0
      CLEAR
      PRINT "Error in Generate_Log_Chart()"
      PRINT "Frequencies equal, or entered backwards."
      STOP
   END IF

   ! ******************
   ! Scaling the screen
   ! ******************
   LET xmin = log10(Low_Freq)
   LET xmax = log10(High_Freq)
   LET ymin = Low_Linear
   LET ymax = High_Linear
   LET Delta_Linear = (High_Linear-Low_Linear)/10
   LET MaxCycle = int(log10(High_Freq)-log10(Low_Freq))
   SET WINDOW xmin, xmax, ymin, ymax

   ! *********************
   ! Draw horizontal lines
   ! *********************
   FOR y = ymin to ymax step Delta_Linear
      PLOT xmin,y; xmax,y
   NEXT y

   ! *******************
   ! Draw vertical lines
   ! *******************
   FOR freq = Low_Freq to 10*Low_Freq step Low_Freq

      ! ********************************
      ! Loop through each cycle of chart
      ! ********************************
      FOR Cycle = 0 to MaxCycle

         ! ******************************************************
         ! Draw one vertical line for each tenth multiple of freq
         ! ******************************************************
         LET x = log10(10^Cycle * freq)
         PLOT LINES : x,ymin; x,ymax

      NEXT Cycle

   NEXT freq

END SUB  ! End of Generate_Log_Chart()




! ===================================================================
! SUBROUTINE: Generate_Smith_Chart
! ===================================================================
!
! PURPOSE: To generate Smith chart in viewport defined in
!          calling program.
!
! PARAMETERS:
!
!    INPUT:  Normalized_Z ... Impedance to which user wants chart
!                             normalized
!
!    OUTPUT:  NONE
!
!    IN/OUT:  NONE
!
! SHARED VARIABLES: Zo ...... This routine assigns value of 
!                             Normalized_Z to Zo
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: NONE
!
! POSTCONDITIONS: Viewport scaled to Smith chart coordinates
!
! ROUTINES CALLED:   X_Arc()
!
! PSEUDOCODE:
!
!   1 - Test for zero INPUT
!
!   2 - Assign INPUT to SHARED variable Zo
!
!   3 - Scale viewport
!
!   4 - Draw outside circumference of chart
!
!   5 - LOOP to draw various resistance circles and reactance arcs
!
!      a) Read normalized value from DATA
!
!      b) Calculate radius of resistance circle
!
!      c) Draw resistive circle
!
!      d) Calculate radius of reactance arc
!
!      e) CALL X_Arc() to draw reactance arcs
!
!      f) Repeat (a) until out of DATA
!
!
! DIAGRAMS:
!
!   Generate_Smith_Chart()
!
!           |
!           |
!           |
!            ----> X_Arc()
!
!                    |
!                    |
!                    |
!                     -----> Draw_X_Arc()
!
!
! ===================================================================
SUB Generate_Smith_Chart(Normalized_Z)

    ! **************************************
    ! Test INPUT and normalize chart
    ! Zo is a SHARED variable in this module
    ! **************************************
    IF Normalized_Z = 0 THEN

       WINDOW #0
       CLEAR
       PRINT "Error in Generate_Smith_Chart()"
       PRINT "Received zero normalization resistance."
       STOP

    ELSE

       ! *****************************************************
       ! Assign normalization resistance to SHARED variable Zo
       ! *****************************************************
       LET Zo = Normalized_Z

    END IF

    ! **************
    ! Scale viewport
    ! **************
    LET XLeft   =  2
    LET XRight  =  0
    LET YBottom = -1
    LET YTop    =  1
    LET XMid    =  1
    LET YMid    =  0
    SET WINDOW XLeft, XRight, YBottom, YTop

    ! ***********************************************
    ! Draw outside circle, real axis, and center spot
    ! ***********************************************
    PLOT XLeft,YMid; XRight,YMid
    BOX CIRCLE XLeft, XRight, YBottom, YTop
    BOX CIRCLE XMid-.02, XMid+.02, YMid-.02, YMid+.02

    ! ******************************
    ! Smith chart loop
    ! Draw circles and arcs of chart
    ! ******************************
    DO

       ! *************
       ! Radius number
       ! *************
       READ RN

       ! *************************************
       ! Calculate radius of resistance circle
       ! *************************************
       LET RealRadius = 1/(1+RN)

       ! **********************
       ! Draw resistance circle
       ! **********************
       BOX CIRCLE 2*RealRadius,0,-RealRadius,RealRadius

       ! *********************************
       ! Calculate radius of reactance arc
       ! *********************************
       LET ImagRadius = 1/RN

       ! ***********************************
       ! CALL routine to draw reactance arcs
       ! ***********************************
       CALL X_Arc((RN))

    LOOP until end data  ! End of Smith chart loop

    DATA .2, .5, 1, 2, 5

END SUB  ! End of Generate_Smith_Chart()
    
    

    
! ===================================================================
! SUBROUTINE: X_Arc                            PRIVATE to this module
! ===================================================================
!
! PURPOSE: To help calling routine, Generate_Smith_Chart(), by
!          invoking Draw_X_Arc, which draws reactive arcs on
!          the Smith chart.  This routine calls Draw_X_Arc()
!          twice, once for upper and once for lower arcs.
!
! PARAMETERS:
!
!    INPUT: XRAD ... Radius of arc to be drawn.
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES: NONE
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: NONE
!
! POSTCONDITIONS: NONE
!
! ROUTINES CALLED: Draw_X_Arc()
!
! PSEUDOCODE:
!
!   1 - Call Draw_X_Arc() to draw upper reactive arc.
!           a) Pass XRAD, radius of arc
!           b) Pass 1 for upper arc
!
!   2 - Call Draw_X_Arc() to draw lower reactive arc.
!
!           a) Pass XRAD, radius of arc
!           b) Pass -1 for lower arc
!
! DIAGRAMS:
!
! ===================================================================
SUB X_Arc(XRAD)

    ! Draw upper reactive arc of Smith chart
    !
    CALL Draw_X_Arc(XRAD, 1)

    ! Draw lower reactive arc of Smith chart
    !
    CALL Draw_X_Arc(XRAD,-1)

END SUB  ! End of X_Arc()




! ===================================================================
! SUBROUTINE: Draw_X_Arc                       PRIVATE to this module
! ===================================================================
!
! PURPOSE: To draw reactive arcs on Smith chart.
!
! PARAMETERS:
!
!    INPUT:    XRAD .............. Radius of arc
!
!              SIGN .............. Determines if routine draws lower
!                                  or upper arc
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES: NONE
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: NONE
!
! POSTCONDITIONS: NONE
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!   1) Specify degrees measure
!
!   2) Calculate dAngle, inverse of XRAD.  The smaller the radius,
!      the larger the angular step size, the faster the drawing.
!      Larger radius uses smaller step size and gets drawn more
!      accurately.
!
!   3) Start at -90 degrees
!
!   4) Calculate (x,y) coordinates for first point at right edge
!      of Smith chart.  Note that this is the origin (0,0) of
!      the coordinate system.
!
!   5) Plot first point
!
!   6) Enter loop to draw an arc from the first point to the
!      circumference of the chart.
!
!      a) Increment Angle
!
!      b) Calculate new (x,y) coordinate pair.
!
!      c) Use Pythagorean theorem to find out if the point is
!         outside the chart?
!
!         YES - Point is outside the chart
!
!               1) Go back to previous angle
!
!               2) Reduce step size, divide by 10.
!
!               3) To see if line is close enough to edge of chart
!                  test if dAngle is less than .01/XRAD.  Exit loop
!                  when dAngle is too small.
!
!         NO - Point is inside the chart
!
!              Draw a line between current point and the last one.
!
!      d) Loop.  Note that loop terminates from the middle, when
!         dAngle is smaller than .01/XRAD.
!
!   7) Arc is finished.  PEN-UP before returning to calling program.
!
! DIAGRAMS:
!
! ===================================================================
SUB Draw_X_Arc(XRAD, SIGN)

    OPTION ANGLE degrees

    ! **********************************
    ! Use inverse of radius as step size
    ! **********************************
    LET dAngle = 1/XRAD

    ! ********************
    ! Start at -90 degrees
    ! ********************
    LET Angle  = -90

    ! ********************************
    ! Calculate first (x,y) coordinate
    ! pair, at right edge of chart.
    ! Right edge is the origin, (0,0)
    ! ********************************
    LET x =  XRAD*COS(Angle)              ! Zero at -90 degrees
    LET y = SIGN*(XRAD+XRAD*SIN(Angle))   ! Zero at -90 degrees
    PLOT x,y;

    ! ***************************************
    ! Loop to draw arc starting at origin and
    ! ending at circumference of chart.
    ! ***************************************
    DO

       ! ***************
       ! Increment Angle
       ! ***************
       LET Angle = Angle+dAngle

       ! ***********************************
       ! Calculate new (x,y) coordinate pair
       ! ***********************************
       LET x = XRAD*COS(Angle)
       LET y = SIGN*(XRAD+XRAD*SIN(Angle))

       ! ***************************
       ! Is point outside the chart?
       ! ***************************
       IF ((x-1)^2 + y^2 ) > 1 THEN

          ! *****************************
          ! YES - Point is outside chart.
          ! Go back to previous angle.
          ! *****************************
          LET Angle = Angle-dAngle

          ! ***********************************************
          ! Reduce step size to get closer to edge of chart
          ! ***********************************************
          LET dAngle = dAngle / 10

          ! ********************************************
          ! Is step size too small, if YES quit the loop
          ! ********************************************
          IF dAngle < .01/XRAD THEN EXIT DO

       ELSE 

          ! *******************************************
          ! NO.  Point is still inside the chart.  Draw
          ! a line between current and previous points.
          ! *******************************************
          PLOT x,y;
 
       END IF

    LOOP

    ! ***********************************************
    ! Lift pen from drawing surface before returning.
    ! ***********************************************
    PLOT

END SUB  ! End of Draw_X_Arc()




! ===================================================================
! SUBROUTINE: SmithPlot()
! ===================================================================
!
! PURPOSE: To Plot an impedance on the Smith chart.
!
! PARAMETERS:
!
!    INPUT:  Rs ..... Series resistance of impedance to plot
!
!            Xs ..... Series reactance of impedance to plot
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES:
!
!       Zo .... Resistance to normalize chart.  This routine only
!               reads the value.
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: NONE
!
! POSTCONDITIONS: NONE
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!   1) Test for negative resistance.  This routine clears the viewport
!      and stops program execution if a resistance is negative.  Of
!      course you can design the routine to handle negative resistances
!      if you want to.
!
!   2) Normalize the impedance to Zo.  We do this by dividing the
!      the series resistance and reactive values by Zo.
!
!   3) Calculate radius of reactive arc.  Make it a very high
!      value if the reactance is zero.
!
!   4) Calculate radius of resistive circle.
!
!   5) Calculate angle between radius of reactive arc to 
!      intersection point and the vertical axis.
!
!   6) Calculate coordinates of intersection of reactive arc
!      and resistive circle.
!
!   7) Display cross at point in chart that represents the impedance.
!      Do not lift pen before returning to calling program, otherwise
!      you will loose interconnection lines between data points.
!      
!
! DIAGRAMS:
!
! ===================================================================
SUB SmithPlot(Rs,Xs)

    OPTION ANGLE degrees

    ! ************************************
    ! I don't handle negative resistances,
    ! but of course you can if you like.
    ! ************************************
    IF Rs < 0 THEN
       WINDOW #0
       CLEAR
       PRINT "Error in SmithPlot() routine."
       PRINT "Negative resistance."
       STOP
    END IF

    ! **************************************
    ! Normalize to Zo Ohms
    ! Zo is a SHARED variable in this module
    ! **************************************
    LET Rn = Rs/Zo
    LET Xn = Xs/Zo

    ! *******************************************
    ! Calculate radius of reactive arc.
    ! If reactance is zero make radius very high.
    ! *******************************************
    IF Xn = 0 THEN
       LET RADX = 1000000
    ELSE
       LET RADX = 1/Xn
    END IF

    ! *************************************
    ! Calculate radius of resistive circle.
    ! *************************************
    LET RADR = 1/(1+Rn)

    ! ***************************************
    ! Calculate angle between reactive radius 
    ! drawn to intersection and vertical axis.
    ! ***************************************
    LET Theta = 2 * ATN(RADR/RADX)

    ! *************************************************************
    ! Calculate coordinates of intersection of resistive and
    ! reactive arcs.  Use Pythagorean theorem of right triangle.
    ! *************************************************************
    LET x = SQR(RADX^2 - (RADX * COS(Theta))^2)
    LET y = RADX - RADX * COS(Theta)

    ! ***************************************
    ! Display cross for single color displays
    ! ***************************************
    PLOT x,y
    PLOT x-.025,y+.025; x+.025,y-.025
    PLOT x-.025,y-.025; x+.025,y+.025
    PLOT x,y;

END SUB  ! End of SmithPlot()




! ===================================================================
! SUBROUTINE: PrintZ()
! ===================================================================
!
! PURPOSE: To print impedance to screen using the traditional j
!          symbol for imaginary, or reactive part, which is popular
!          in engineering, rather than the i symbol, which is
!          popular in mathematics.
!
! PARAMETERS:
!
!    INPUT:  
!
!      r .... Resistive (real) part of impedance in rectangular form.
!              
!      x .... Reactive (imaginary) part of impedance in rectangular form.
!
!      ver .. Vertical character position, measured from top of screen.
!
!      hor .. Horizontal character position, from left edge of screen.
!
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES: NONE
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: NONE
!
! POSTCONDITIONS: NONE
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!   1) Set cursor postion on screen
!
!   2) Clear space on screen for 30 characters.
!
!   3) Display resistive and reactive parts.  Use True BASIC STR$()
!      function to remove leading spaces before printing.
!
! DIAGRAMS:
!
! ===================================================================
SUB PrintZ(r, x, ver, hor)

    ! ***************
    ! Position cursor
    ! ***************
    SET CURSOR ver, hor

    ! **********************************
    ! Clear area for up to 30 characters
    ! and reposition cursor.
    ! **********************************
    PRINT REPEAT$(" ", 30)
    SET CURSOR ver, hor

    ! ********************
    ! Print resistive part
    ! ********************
    PRINT "Z = ";
    PRINT STR$(r);

    ! **************************************************
    ! Test for positive or negative reactance and print.
    ! **************************************************
    IF x < 0 then
       PRINT " -j";
    ELSE
       PRINT " +j";
    END IF
    PRINT STR$(ABS(x))

END SUB  ! End of PrintZ()




! ===================================================================
! SUBROUTINE: Scale_Log_Chart
! ===================================================================
!
! PURPOSE: To scale viewport in calling program to coordinates of
!          log chart saved in graphic file LOG.GRF.
!
! PARAMETERS:
!
!    INPUT:
!
!        Low_Freq ..... Log lower limit
!        High_Freq .... Log upper limit
!        Low_Linear ... Linear lower limit
!        High_Linear .. Linear upper limit
!
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES: NONE
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: Make sure desired viewport is active in calling program
!
! POSTCONDITIONS: Active viewport scaled to log chart limits
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!   1) Error check frequency range.
!
!   2) Scale the viewport
!
! DIAGRAMS:
!
! ===================================================================
SUB Scale_Log_Chart(Low_Freq, High_Freq, Low_Linear, High_Linear)

    ! **************************
    ! Trap negative frequencies.
    ! **************************
    IF Low_Freq < 0 or High_Freq < 0 THEN
       WINDOW #0
       CLEAR
       PRINT "Error in Scale_Log_Chart()"
       PRINT "Negative frequency"
       STOP
    END IF

    ! **************************
    ! Trap backwards frequencies.
    ! **************************
    IF Low_Freq > High_Freq THEN
       WINDOW #0
       CLEAR
       PRINT "Error in Scale_Log_Chart()"
       PRINT "Frequencies are backwards."
       STOP
    END IF

    ! ****************************
    ! Trap equal frequency limits.
    ! ****************************
    IF Low_Freq > High_Freq THEN
       WINDOW #0
       CLEAR
       PRINT "Error in Scale_Log_Chart()"
       PRINT "Frequency limits equal."
       STOP
    END IF

    ! ***********************************
    ! Scale the currently active viewport
    ! ***********************************
    LET xmin = log10(Low_Freq)
    LET xmax = log10(High_Freq)
    LET ymin = Low_Linear
    LET ymax = High_Linear
    SET WINDOW xmin, xmax, ymin, ymax

END SUB  ! End of Scale_Log_Chart()




! ===================================================================
! SUBROUTINE: Scale_Smith_Chart
! ===================================================================
!
! PURPOSE: To scale viewport in calling program to coordinates of
!          Smith chart saved in graphic file SMITH.GRF.
!
! PARAMETERS:
!
!    INPUT:  Z_Normalized .. Value of impedance to which 
!                            to normalize Smith chart.
!
!    OUTPUT: NONE
!
!    IN/OUT: NONE
!
! SHARED VARIABLES:  Zo ... Resistance to which Smith chart is to be
!                           normalized.  Desired value is passed from
!                           calling program and assigned to SHARED
!                           variable by this routine.
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: Make sure desired viewport is active in calling program
!
! POSTCONDITIONS: Active viewport scaled to Smith chart coordinates
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!   1) Trap undesired values of normalization resistance.
!
!   2) Scale viewport in calling program.
!
! DIAGRAMS:
!
! ===================================================================
SUB Scale_Smith_Chart(Z_Normalized)

    ! *********************
    ! Trap undesired values
    ! *********************
    IF Z_Normalized <= 0 THEN
       WINDOW #0
       CLEAR
       PRINT "Error in Scale_Smith_Chart()"
       PRINT "Negative or zero normalization resistance."
       STOP
    END IF

    ! **************************************************
    ! Assign normalization resistance to SHARED variable
    ! **************************************************
    LET Zo = Z_Normalized

    ! *********************************
    ! Scale viewport in calling program
    ! *********************************
    LET XMin =  2
    LET XMax =  0
    LET YMin = -1
    LET YMax =  1
    LET XMid =  1
    LET YMid =  0
    SET WINDOW XMin, XMax, Ymin, Ymax

END SUB  ! End of Scale_Smith_Chart()




! ===================================================================
! FUNCTION: Graphic$()
! ===================================================================
!
! PURPOSE: To read graphic data stored on disk and return it to
!          calling program as a string.
!
! PARAMETERS: Filename$ ... name of file in which data has been
!                           previously stored.
!
! RETURNED VALUE: String containing graphic data
!
! SHARED VARIABLES: NONE
!
! GLOBAL VARIBLES: NONE
!
! PRECONDITIONS: Make sure desired viewport in calling program is active.
!
! POSTCONDITIONS: NONE
!
! ROUTINES CALLED: NONE
!
! PSEUDOCODE:
!
!   1) Open Filename$ as BYTE file for INPUT
!
! DIAGRAMS:
!
! ===================================================================
FUNCTION Graphic$(Filename$)

    ! *****************************************
    ! Open the file containing the graphic data
    ! *****************************************
    OPEN  #1 : NAME Filename$, ACCESS INPUT, ORGANIZATION BYTE

    ! *************************
    ! READ the data in the file
    ! *************************
    ASK   #1 : FILESIZE length
    READ  #1, bytes length : A$

    ! **************
    ! Close the file
    ! **************
    CLOSE #1

    ! ****************************************
    ! Return the string to the calling program
    ! ****************************************
    LET Graphic$ = A$

END FUNCTION  ! End of Graphic$()


END MODULE
