; ANTENNA ANALYZER by James Tregellas VK5JST 'This program is fully protected by the provisions of the 'Commonwealth Copyright Act (Australia). Although any form 'of reproduction contravenes the Act, the author is not concerned 'about bona fide electronic hobbyists (and particularly radio 'amateurs) making single copies of it for their own personal 'use. However, commercial organisations should note that no 'significant part of this software can be offered for sale or 'sold, either on its own or as part of an assembly, unless a 'licence to do so has been obtained from myself-James Tregellas, '14 Sheringa Drive, Morphett Vale 5162 July 2004 'Software modified by Lee de Vries VK3PK (December 2004) 'rewrote it to allow PICAXE28X with maximum 16 gosubs (low PICAXE firmware numbers <7.4) 'by rewriting the string handling & removing some redundant code 'it is still possible to have 5 more gosubs 'remove leading 0's on frequency 'added some variable names & indentation/documentation 'added serial output ( for capture to PC & Text2Speech for visually disabled use) 'reduced code from 1788 bytes to approx 1318 bytes 'Desireable mods - Electronic shutdown to prevent flat batteries ' (bit of a shortage of variables) symbol COUNT_INTERVAL_FINE = 4020 'Approx 1 seconds to resolve symbol COUNT_INTERVAL_COURSE = 402 'Approx 0.1 seconds to resolve symbol BATTERY_DISP_TIME = 6000 'Battery voltage display parameter symbol BATTERY_CALIBRATION = 99 'Battery voltage calibration factor (range 94-106) 'compensates for the 16K/3.9K component tolerances. 'LCD display layout 'FREQUENCY R=xxxx 'X=xxxx SWR=xxxx 'constant declarations symbol MINRFIN = 50 symbol MINRFCURRENT = 10 symbol MINRFOUT = 10 symbol LINEA = 128 '0x80 symbol LINEB = 192 '0xc0 symbol OHM = 244 '0xf4 'pin declarations symbol AN0 = 0 symbol AN1 = 1 symbol AN2 = 2 symbol AN3 = 3 symbol RS = 2 symbol EN = 3 ' variable declarations ' Variable name Var. When used symbol message_pointer = b0 symbol lcd_char = b1 'character output routine symbol Vin = b1 'inputting AN0 symbol temp = b0 'input checking symbol word0 = w0 'b1:b0 used during calculations & holding AN3 symbol freq_count = w0 'during frequency counter symbol V50 = b2 'inputting AN1 symbol Vload = b3 'inputting AN2 symbol word1 = w1 'b3:b2 symbol SWR = w1 symbol word2 = w2 'b5:b4 symbol _B = w3 'b7:b6 symbol _A = w4 'b9:b8 symbol char_ptr = b11 'during character output symbol reactance = w5 symbol resistance = w6 '********************************************************************************** '* * '* STARTUP & INITIALIZE INSTRUMENT * '* * '********************************************************************************** start:'INITIALISE LCD DISPLAY pins=0 'clear all output lines pause 800 'wait 200ms for lcd to reset pins=48 'set to 8 bit operation pulsout EN,4 'send data by pulsing enable line pause 40 'wait 10ms pulsout EN,4 'send data by pulsing enable line pulsout EN,4 'send data by pulsing enable line pins=32 'set to 4 bit operation pulsout EN,4 'send data by pulsing enable line pulsout EN,4 'send data by pulsing enable line pins=128 'set to 2 line operation pulsout EN,4 'send data by pulsing enable line lcd_char=12 'screen on, cursor on operation gosub wrcmd 'write instruction to lcd sertxd(0x0d,0x0a) '********************************************************************************** '* * '* DO BATTERY TEST * '* * '********************************************************************************** readadc AN3,b1 'External resistors are scaled to give a resulting 'count of around 120 for a 12volt battery - 16K from +12volt to adc input 3 ' and 3K9 from adc input 3 to ground. w0=b1*100 'adjust the adc count by multiplication and then division so that w0=w0/BATTERY_CALIBRATION 'the display shows the correct voltage - change say division only w1=w0/100 w2=w0//100 w2=w2/10 w3=w0//10 w1=w1+48 'only low order bytes are converted to ASCII w2=w2+48 w3=w3+48 message_pointer = 0 goto message battery_pause: pause BATTERY_DISP_TIME 'display battery volts for specifies time '********************************************************************************** '* * '* COUNT & DISPLAY FREQUENCY * '* * '********************************************************************************** main: lcd_char=LINEA gosub wrcmd if pin2 =1 then course_freq 'digital input 2 (IC pin13) 'Frequency counter routine. 5 digit resolution count 3,4020,freq_count 'Max input frequency is 50KHz on digital input 3 (IC pin14)- w1=freq_count/10000 'NOTE-careful measurement shows 100KHz published is WRONG. w2=freq_count//10000 w2=w2/1000 w3=freq_count//1000 'Routines with w1,w2,w3,w4,w6 recover individual digits from count total freq_count. w3=w3/100 'Clock rate used is 16MHz. Prescaler divider ratio used to feed input 3 is 1024. w4=freq_count//100 w4=w4/10 w6=freq_count//10 'Higher precision message_pointer =0 goto dispfreq: course_freq: 'Frequency counter routine. 4 digit resolution count 3,402,freq_count w1=freq_count/1000 'Comments as for preceding routine w2=freq_count//1000 w2=w2/100 w3=freq_count//100 w3=w3/10 w4=freq_count//10 message_pointer=2 dispfreq: w1=w1 + 48 'only low order bytes are converted to ASCII w2=w2 + 48 w3=w3 + 48 w4=w4 + 48 w6=w6 + 48 if b2 = 48 then leading0 message_pointer = message_pointer + 1 leading0: char_ptr = message_pointer * 8 message_pointer = char_ptr + 8 nextf: lookup char_ptr,(b4,".",b6,b8,b12,"MHz",b2,b4,".",b6,b8,"MHz",b4,".",b6,b8," MHz",b2,b4,".",b6," MHz"),lcd_char sertxd(lcd_char) gosub wrchr ' transmit value to serial LCD char_ptr = char_ptr+1 if char_ptr < message_pointer then nextf sertxd(0x20) '********************************************************************************** '* * '* CALCULATE & DISPLAY RESISTANCE & REACTANCE * '* * '********************************************************************************** 'Three voltages are read from network via ADC inputs AN0(ICpin2),AN1(ICpin3) & AN2(ICpin4) 'and used to calculate magnitudes of complex and real parts of antenna load impedance. readadc AN0,Vin 'Vin=network input volts readadc AN1,V50 'V50=voltage proportional to network current readadc AN2,Vload 'Vload=network output voltage ' *************************************************************************************** 'debug 'This is a great place for "debug", for adjustments etc.* ' *************************************************************************************** if Vin = Vin then valid_voltages Vin = V50 + Vload 'modify Vin and treat as pure resistance valid_voltages: w2=Vin * Vin 'Vin squared w3=V50 * V50 'V50 squared w4=Vload * Vload 'Vload squared w5=w3 + w4 'V50 squared + Vload squared 'check for impossible output from network if w2>=w5 then gotvolts ' impossible output from network(<90 degree triangle .Modify w4 and treat as pure X let w2=w3+w4 'pure X Modify w4 gotvolts: 'Calculate voltage accross resistive component using Equation 1 (see cct analysis) ' As there are two unknowns we need to set up two simultaneous equations and solving them. ' The two equations are set up using the Pythagoras theorem w2=w2-w3-w4 'Vin squared - V50 squared - Vload squared w2=w2/2 w2=w2/V50 ' Divided by 2 X V50 'w2 now equals Vr**2 - the voltage across resistance component of load squared 'Calculate voltage accross the resistive component using Equation 1 (see cct analysis) w5=w2*w2 'Vr squared w5=w4-w5 'w5 now equals Vx**2 - the voltage across the reactive component of load squared 'square root routine (using Naperian successive approximation) Newtons method ???? w0=V50 ' save voltage across 50Ohm resistor w3=w5 ' save voltage across unknown reactance w4=0 ' initialize variables w1=0 w3=w3/2 'first approximation of square root refine_sqrt: w4=w5/w3 w4=w4+w3 w4=w4/2 if w4=w1 then calc_rx 'w4=voltage across unknown reactance w1=w1+1 'With integer values routine does not always if w4=w1 then calc_rx 'converge to a single value,but can oscillate w1=w4 'by plus or minus one. w3=w4 goto refine_sqrt calc_rx: w4=w4*50 w4=w4/w0 'w4=reactance in ohms w2=w2*50 w2=w2/w0 'w2=resistance in ohms 'print resistance message_pointer = 0 if w2>999 then resistance_lcd w0=w2 'recover individual digits from w2 (resistance) resistance=w2 'make a copy of resistance for later on (SWR calculation) w1=w0/100 w2=w0//100 w2=w2/10 w3=w0//10 w1=w1+48 'only low order bytes are converted to ASCII w2=w2+48 w3=w3+48 message_pointer = 1 resistance_lcd: char_ptr = message_pointer * 8 message_pointer = char_ptr+8 next_resistance: lookup char_ptr,(" R>999"," ",OHM," R=",b2,b4,b6," ",OHM),lcd_char sertxd(lcd_char) gosub wrchr char_ptr = char_ptr+1 if char_ptr < message_pointer then next_resistance sertxd(0x20,0x20) ' print reactance on line 2 gosub newline2 message_pointer = 0 'w4=voltage across unknown reactance if w4>999 then reactance_lcd w0=w4 'Recover individual digits from w4 (impedance) w1=w0/100 w2=w0//100 w2=w2/10 w3=w0//10 w1=w1+48 'only low order bytes are converted to ASCII w2=w2+48 w3=w3+48 message_pointer = 1 reactance_lcd: char_ptr = message_pointer * 8 message_pointer = char_ptr+8 next_reactance: lookup char_ptr,("X>999"," ",OHM," ","X=",b2,b4,b6," ",OHM," "),lcd_char sertxd(lcd_char) gosub wrchr char_ptr = char_ptr+1 if char_ptr < message_pointer then next_reactance sertxd(0x20) 'R still in w6 & X still in w4 '********************************************************************************** '* * '* CALCULATE & DISPLAY SWR * '* * '********************************************************************************** reactance=w4 if resistance=0 then hi_swr 'if resistance = 0 then display SWR>10 if resistance<50 then low_r 'if resistance <50R then use modified equations to avoid negative numbers if resistance=50 then z0 'if resistance = 50R avoid square root of zero in equations if resistance<150 then med_r 'if resistance >50R but <50R, use standard equations for max. accuracy ' R>150 OR X>150 high_r_or_z: resistance=resistance/6 'if R or X >150R, use modified standard reactance=reactance/6 'scale resistance & reactance to avoid 16 bit overflows w4=resistance+8 w4=w4*w4 w2=reactance*reactance w4=w4+w2 'square root to find _A w1=0 w2=w4 w0=w4/2 'first approximation of square root opt_A1:w0=w2/w0 w0=_A+w0 w0=w0/2 if w0=w1 then donesqrt_A1 w1=w1+1 'With integer values routine does not always if w0=w1 then donesqrt_A1 'converge to a single value,but can oscillate w1=w0 'by plus or minus one. _A=w0 goto opt_A1 'refine root donesqrt_A1: w3=resistance-8 w3=w3*w3 w2=reactance*reactance w3=w3+w2 goto sqrt_B 'R<50 low_r: if reactance>150 then hi_swr w4=resistance+50 'R<50 and X<=150 w4=w4*w4 w2=reactance*reactance w4=w4+w2 'square root to find _A w1=0 w2=w4 w0=w4/2 'first approximation of square root opt_A2:w0=w2/w0 w0=_A+w0 w0=w0/2 if w0=w1 then donesqrt_A2 w1=w1+1 'With integer values routine does not always if w0=w1 then donesqrt_A2 'converge to a single value,but can oscillate w1=w0 'by plus or minus one. _A=w0 goto opt_A2 'refine root donesqrt_A2: w3=50-resistance w3=w3*w3 w2=reactance*reactance w3=w3+w2 goto sqrt_B 'R=50 z0: if reactance=0 then resonant if reactance>150 then hi_swr w3=reactance 'R=50 & X<=150 w4=resistance+50 w4=w4*w4 w2=reactance*reactance w4=w4+w2 'square root to find _A w1=0 w2=w4 w0=w4/2 'first approximation of square root opt_A3:w0=w2/w0 w0=_A+w0 w0=w0/2 if w0=w1 then donesqrt_A3 w1=w1+1 'With integer values routine does not always if w0=w1 then donesqrt_A3 'converge to a single value,but can oscillate w1=w0 'by plus or minus one. _A=w0 goto opt_A3 'refine root donesqrt_A3: goto calcswr resonant: 'X = 0 ie. XL=XC SWR=100 goto swr_lcd ' 50150 then high_r_or_z w4=resistance+50 ' 509.99"),lcd_char sertxd(lcd_char) gosub wrchr char_ptr = char_ptr+1 if char_ptr < message_pointer then next_swr goto main '********************************************************************************** '* * '* MISC MESSAGES * '* * '********************************************************************************** 'MISC MESSAGES always sent on line 2 no_rf_osc: 'error message for no rf drive to test cct message_pointer = 1 goto message open: 'message for no load on test cct message_pointer = 2 goto message short: 'message for short on test cct message_pointer = 3 message: 'clear the second half of line 1 for char_ptr = 0 to 7 lcd_char=0x20 gosub wrchr next char_ptr ;enter here with message number in "message_pointer" ;all messages should be 16 characters long gosub newline2 char_ptr = message_pointer * 16 message_pointer = char_ptr + 16 nextmptr: lookup char_ptr,("BATTERY=",b2,b4,".",b6,"V ","NO RF OSCILLATOR"," OPEN CIRCUIT "," SHORT CIRCUIT "),lcd_char sertxd(lcd_char) gosub wrchr char_ptr = char_ptr+1 if char_ptr < message_pointer then nextmptr if char_ptr = 16 then battery_pause: goto main 'error therefore SWR cannot be calculated '********************************************************************************** '* * '* LCD & DISPLAY * '* * '********************************************************************************** newline2: lcd_char= LINEB wrcmd: 'WRITE COMMAND subroutine pins=lcd_char & 240 'mask high nibble of lcd_char into b2 pause 4 pulsout EN,4 'send data by pulsing enable line lcd_char = lcd_char * 16 'put low nibble of lcd_char into lcd_char pins=lcd_char & 240 'mask the high nibble of lcd_char pause 4 pulsout EN,4 'send data by pulsing enable line return 'return to main program wrchr: 'WRITE CHARACTER subroutine pins=lcd_char & 240 'mask high nibble of lcd_char into b2 high RS pause 4 pulsout EN,4 'send data by pulsing enable line lcd_char = lcd_char * 16 'put low nibble of lcd_char into lcd_char pins=lcd_char & 240 'mask the high nibble of lcd_char high RS pause 4 pulsout EN,4 'send data by pulsing enable line return 'return to main program '********************************************************************************** '* * '* END * '* * '**********************************************************************************