; AMSAT/TAPR DSP BPSK demod for PIII satellites ; Sample rate should be set to 8000 samples/sec. ; code by Bob McGwier N4HY ; org 0 b go ; ; Okay Jose' set up equates for data memory etc. ; org 10H ; code should begin at memory address 16 cause David ; does something strange with locations 0-15. Does ; he in fact document this? sine: equ 0 ; will store sine values from tone one: equ 1 ; guess what goes here duhhhhh! freq: equ 2 ; PLL frequency stored here phase: equ 3 ; PLL phase stored here maskl: equ 4 ; mask for fine or low order bits of phase mask: equ 5 ; mask for doing modulo 16384 arithmetic with phase sinx: equ 6 ; used to store coarse sine value (high order bits) cosx: equ 7 ; as above for cosine mone: equ 8 ; minus one stored here wkph: equ 9 ; different phases (PLL, remodulator tone etc) are stored hr ; for frequency synthesis masko: equ 10 ; mask for converting on board PCM to DAC format mps: equ 11 ; multiplier for quadrant determination for sine mpc: equ 12 ; multiplier for quadrant determination for cosine siny: equ 13 ; fine correction value for use in SIN(X+Y) = sinx*cosy+ cosy: equ 14 ; cosx*siny cosine: equ 15 ; cosine is also needed for Q arm in Costas loop coph: equ 16 ; working number holder modem: equ 17 ; Used to choose between complex tone for arms and real tone ; in the modulator as explained below bitpc: equ 20 ; freqo is assigned one of the values above for remod xn0: equ 21 ; place for storing all the values for the "I" or data arm xn1: equ 22 xn2: equ 23 xn3: equ 24 xn4: equ 25 xn5: equ 26 xn6: equ 27 xn7: equ 28 xn8: equ 29 xn9: equ 30 xn10: equ 31 xn11: equ 32 xn12: equ 33 xn13: equ 34 xn14: equ 35 xn15: equ 36 xn16: equ 37 xn17: equ 38 xn18: equ 39 xn19: equ 40 xn20: equ 41 xn21: equ 42 xn22: equ 43 yn0: equ 44 ; storage for values in "Q" or phase error arm. yn1: equ 45 yn2: equ 46 yn3: equ 47 yn4: equ 48 yn5: equ 49 yn6: equ 50 yn7: equ 51 yn8: equ 52 yn9: equ 53 yn10: equ 54 yn11: equ 55 yn12: equ 56 yn13: equ 57 yn14: equ 58 yn15: equ 59 yn16: equ 60 yn17: equ 61 yn18: equ 62 yn19: equ 63 yn20: equ 64 yn21: equ 65 yn22: equ 66 c0: equ 67 ; Phase error maskf: equ 68 ; frequency mask so that frequency doesn't try and leave ; proper range H9: equ 69 H10: equ 70 ; arm filter value too large to use mpyk H11: equ 71 ; arm filter value too large to use mpyk sweep: equ 74 ; contains sweep value +/- one frequency unit per sample highfq: equ 75 ; high end of sweep range lowfq: equ 76 ; low end of sweep range clkfr: equ 78 clkph: equ 79 ch4: equ 80 ch5: equ 81 ch6: equ 82 bd0: equ 83 bd1: equ 84 bd2: equ 85 bd3: equ 86 bd4: equ 87 bd5: equ 88 bd6: equ 89 bd7: equ 90 bd8: equ 91 bd9: equ 92 bd10: equ 93 bd11: equ 94 bd12: equ 95 cd0: equ 96 cd1: equ 97 cd2: equ 98 cd3: equ 99 cd4: equ 100 cd5: equ 101 cd6: equ 102 cd7: equ 103 cd8: equ 104 cd9: equ 105 cd10: equ 106 cd11: equ 107 cd12: equ 108 clkav: equ 109 bit: equ 110 clock: equ 111 clocke: equ 112 bito: equ 115 bit0: equ 18 bit1: equ 19 bita0: equ 113 bita1: equ 114 clkit: equ 116 bitm0: equ 117 bitm1: equ 118 tbit: equ 119 tclk: equ 120 tfrq: equ 121 tph: equ 122 tcnt: equ 123 carof: equ 124 inty: equ 125 eom: equ 126 sintbl: dw 0 ; coarse sine table in steps of PI/64 radians to PI/2 dw 804 dw 1607 dw 2410 dw 3211 dw 4011 dw 4807 dw 5601 dw 6392 dw 7179 dw 7961 dw 8739 dw 9511 dw 10278 dw 11038 dw 11792 dw 12539 dw 13278 dw 14009 dw 14732 dw 15446 dw 16150 dw 16845 dw 17530 dw 18204 dw 18867 dw 19519 dw 20159 dw 20787 dw 21402 dw 22004 dw 22594 dw 23169 dw 23731 dw 24278 dw 24811 dw 25329 dw 25831 dw 26318 dw 26789 dw 27244 dw 27683 dw 28105 dw 28510 dw 28897 dw 29268 dw 29621 dw 29955 dw 30272 dw 30571 dw 30851 dw 31113 dw 31356 dw 31580 dw 31785 dw 31970 dw 32137 dw 32284 dw 32412 dw 32520 dw 32609 dw 32678 dw 32727 dw 32757 dw 32767 ; PI/2 fines: dw 0 ; fine sine table in steps of PI/(64*64) radians to PI/64 dw 12 dw 25 dw 37 dw 50 dw 62 dw 75 dw 87 dw 100 dw 113 dw 125 dw 138 dw 150 dw 163 dw 175 dw 188 dw 201 dw 213 dw 226 dw 238 dw 251 dw 263 dw 276 dw 289 dw 301 dw 314 dw 326 dw 339 dw 351 dw 364 dw 376 dw 389 dw 402 dw 414 dw 427 dw 439 dw 452 dw 464 dw 477 dw 490 dw 502 dw 515 dw 527 dw 540 dw 552 dw 565 dw 578 dw 590 dw 603 dw 615 dw 628 dw 640 dw 653 dw 665 dw 678 dw 691 dw 703 dw 716 dw 728 dw 741 dw 753 dw 766 dw 779 dw 791 finec: dw 32767 ;ditto to above for fine sine dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32766 dw 32765 dw 32765 dw 32765 dw 32765 dw 32765 dw 32765 dw 32765 dw 32765 dw 32764 dw 32764 dw 32764 dw 32764 dw 32764 dw 32764 dw 32764 dw 32763 dw 32763 dw 32763 dw 32763 dw 32763 dw 32762 dw 32762 dw 32762 dw 32762 dw 32762 dw 32761 dw 32761 dw 32761 dw 32761 dw 32760 dw 32760 dw 32760 dw 32760 dw 32759 dw 32759 dw 32759 dw 32759 dw 32758 dw 32758 dw 32758 dw 32758 dw 32757 dw 32757 armfilt: dw 5054 ; H9 dw 7679 ; H10 dw 8706 ; H11 dw 4769 ; CH4 dw 6557 ; CH5 dw 7249 ; CH6 go: ldpk 0 ; make sure that we are pointing to 0 data page larp ar0 lark ar0,1 lark ar1,19 lack one ; make and store one sacl one lack armfilt ; store address of the too large coefficients tblr H9 add one tblr H10 add one tblr H11 add one tblr CH4 add one tblr ch5 add one tblr ch6 lac one,11 ; make and store DAC converter mask sacl masko lac one,13 ; make and store frequency mask sub one sacl maskf lt one mpyk 3072 ; 1500Hz pac sacl freq lt one mpyk 4095 ; make and store high end of sweep range pac sacl highfq lt one mpyk 2400 ; make and store low end of sweep range pac sacl lowfq zac sub one sacl mone ; make and store minus one lac one,14 sub one sacl mask ; make and store mask for modulo 16384 phase arithmetic lac one,6 sub one sacl maskl ; make and store fine part of address mask; zac sacl inty sacl phase sacl clkph sacl tph sacl clkph lac one sacl sweep lt one mpyk 1638 ; make and store clock rate (1638 = 800 Hz) pac sacl clkfr lt one mpyk 3072 pac sacl tfrq ; Okay the BS is over lets get to work ! wait: bioz fire ; is it time for a new sample b wait ; nope go wait in the corner fire: in xn0,pa3 ; get a new sample here lac xn0,4 sub one,15 ; Change ADC format to two's complement sacl xn0 ; modulator/status/misc output lac bito,12 addh masko sach yn0 out yn0,pa4 ; send bito to DAC out bitpc,pa7 ; output control word to PC lac inty ; load the PC interrupt control word bz noi zac ; yes sacl inty out 6,6 ; tell the PC that we are ready ; end output ; Determine transmit bit timing noi: larp 1 ; transmit timing auxiliary register banz noty ; is time to tick the clock for the PC? lark ar1,19 ; yes,reset output bit counter zac sacl tcnt ; reset Manchester (split phase) counter lac one,2 ; get ready to tick the clock in the PC control word sacl c0 lac bitpc ; load the control word xor c0 ; tick the clock sacl bitpc ; store the new PC control word lac inty or one sacl inty in c0,7 ; input the PC side control word lac one,4 and c0 ; pick off the eye or modulator control bit sacl eom lac one,3 ; pick off the transmit bit and c0 bgz tpo ; is it zero ? lac mone ; yes set bit multiplier to minus one sacl tbit b noty tpo: lac one ; No it is a one so set the multiplier to one sacl tbit noty: ; Manchester the bit lac tcnt ; Find out if we are half way through the bit sub one,3 sub one,2 ; if tcnt is ten, it is time to split the bit bnz dnch ; if we are not half way, don't change it lt mone ; We are halfway so change the sign of the bit multiplier mpy tbit pac sacl tbit dnch: lac tcnt add one ; Increment ther Manchester (split phase) counter sacl tcnt ; Begin modulator lac eom bnz eye lac mone sacl modem ; real tone lack one ; sacl mps ; Restore sine and cosine quadrant multipliers sacl mpc ; lac tph add tfrq ; increment the modulator phase, tph, by the modulator frq, tfrq and mask sacl tph call tones ; get the sinusoid lt sine mpy tbit ; multiply by the bit pac sacl bito ; store for the DAC to output to the transmitter ; Begin demodulator eye: lack one sacl modem ; store control that makes complex tone lack one ; sacl mps ; Restore sine and cosine quadrant multipliers sacl mpc ; lac phase call tones ; go make complex tone lt xn0 ; load the current sample mpy cosine; multiply by cosine pac sach yn0,1 ; store it Q channel current sample mpy sine ; multiply sample by sine pac sach xn0,1 ; now store it in current sample place for I channel ; Low pass for each sample cutoff is at 800 Hz. Carrier is 20-25 dB down ; First lowpass I arm product to get the lower frequency mixer products zac ; lt xn22 mpyk -117 ; ltd xn21 mpyk 675 ; ltd xn20 mpyk 647 ; ltd xn19 mpyk 325 ; ltd xn18 mpyk -453 ; ltd xn17 mpyk -1303 ; ltd xn16 mpyk -1513 ; ltd xn15 mpyk -459 ; ltd xn14 mpyk 1949 ; ltd xn13 mpy H9 ; ltd xn12 mpy H10 ; ltd xn11 mpy H11 ; ltd xn10 mpy H10 ; ltd xn9 mpy H9 ; ltd xn8 mpyk 1949 ; ltd xn7 mpyk -459 ; ltd xn6 mpyk -1513 ; ltd xn5 mpyk -1303 ; ltd xn4 mpyk -453 ; ltd xn3 mpyk 325 ; ltd xn2 mpyk 647 ; ltd xn1 mpyk 675 ; ltd xn0 mpyk -117 ; apac ; sach xn0,1 ; Now do the Q arm to get the lower frequency mixer products. zac ; lt yn22 mpyk -117 ; ltd yn21 mpyk 675 ; ltd yn20 mpyk 647 ; ltd yn19 mpyk 325 ; ltd yn18 mpyk -453 ; ltd yn17 mpyk -1303 ; ltd yn16 mpyk -1513 ; ltd yn15 mpyk -459 ; ltd yn14 mpyk 1949 ; ltd yn13 mpy H9 ; ltd yn12 mpy H10 ; ltd yn11 mpy H11 ; ltd yn10 mpy H10 ; ltd yn9 mpy H9 ; ltd yn8 mpyk 1949 ; ltd yn7 mpyk -459 ; ltd yn6 mpyk -1513 ; ltd yn5 mpyk -1303 ; ltd yn4 mpyk -453 ; ltd yn3 mpyk 325 ; ltd yn2 mpyk 647 ; ltd yn1 mpyk 675 ; ltd yn0 mpyk -117 ; apac ; sach yn0,1 ; Okay we now have filtered products with data bauding pseudo base banded ; Let's close the loop and lock on lac xn0; Hard limit data line for phase error detector sacl bd0; Input data into delay line so that demanchester can occur at ; Zero crossing. bgez pdps ; is data baud a plus one ? lt yn0 ; get ready for a multiply mpyk -1 ; multiply phase error arm by -1 pac sacl yn0 pdps: lt carof mpyk 7 ; leaky integrate the phase error pac sacl cosy lac carof,15 add cosy,12 ; take 7/8 of the old value and add it to the gain times add yn0,10 ; the current phase error observation. (LPF) sach carof ;; lac carof,3 ; uncomment these two lines to send the ;; sacl bito ; VCO error voltage out the D/A lac yn0,10 ; add a small amount of the current phase error to addh carof ; the integrator output. sach c0 ; this is the phase correction lac c0 ; load correction delph: add phase ; add the old phase add freq ; add the phase increment (frequency) and mask ; modulo 2*pi sacl phase ; store it for next sample complex tone generation step lac eom bz mod lac xn0 ; uncomment these two lines to send eye patterns out sacl bito ; the D/A port ; lac sine,11 ; uncomment these two to hear recovered carrier. ; sach bito ; Assuming lock, let's recover clock using a PLL on a nonlinearity ; generating a clock tone mod: lt xn0 mpy xn0 ; square the current data arm value pac apac apac ; add it to the accumulator three times to raise the number ; of significant bits in the high word sach cd0,1 ; shift left 4 and store the high word lt clkav mpyk 3 ; we will LPF with an exponential decay filter to estimate pac ; the average amplitude of the clock signal. Multiply old sacl cosy lac cd0,13 ; load one sixteenth of the current clock signal value abs add clkav,15 ; value first by fifteen and then divide by 16. add cosy,13 sach clkav lac cd0,2 ;load it shifting one up to increase significance sub clkav,2 sacl cd0 ; subtract the average value, that is remove the DC. ; sacl bito lac mone sacl modem ; real tone lack one ; sacl mps ; Restore sine and cosine quadrant multipliers sacl mpc ; lac one,12 ; we are going to use the tone generator and we need sub clkph ; cosine so take pi/2 - phase and then find the sine of add one,14 ; that after doing mod 2*pi and mask call tones ; get the cosine (which will be the sine since we did ; lac sine,11 ; sach bito ; un comment these two lines to see the clock tone lt sine ; complimentary angle mpy cd0 ; multiply by the current clock tone value pac sach cd0,1 ; filter this product to get the clock signal phase error ; sine(clock phase)*cosine(clock phase estimate) yields the error signal ; LP Filter to get the lower frequency mixer products ; Filter with cutoff at 800 Hz, doesn't need to be a great filter, just ; the best you can do in 13 taps. 25+ dB rejection of the stuff at ; 1600. lt cd12 mpyk -930 pac ltd cd11 mpyk -293 apac dmov bd11 ltd cd10 mpyk 732 apac dmov bd10 ltd cd9 mpyk 2571 apac dmov bd9 ltd cd8 mpy ch4 apac dmov bd8 ltd cd7 mpy ch5 apac dmov bd7 ltd cd6 mpy ch6 apac dmov bd6 ltd cd5 mpy ch5 apac dmov bd5 ltd cd4 mpy ch4 apac dmov bd4 ltd cd3 mpyk 2571 apac dmov bd3 ltd cd2 mpyk 732 apac dmov bd2 ltd cd1 mpyk -293 apac dmov bd1 ltd cd0 mpyk -930 apac dmov bd0 sach clocke lt clkit mpyk 15 ; SLOW leak integrator, 31/32 old phase error plus pac sacl cosy ; lac clkit,15 lac cosy,12 ; add cosy,11 ; lac clkit,11 add clocke,11 ; 1/32 of the new phase error estimate sach clkit lac clocke,10 ; add 1/32 of the current phase error observation to add clkit,15 ; the integrated estimate sach clocke lac clocke ; load phase correction dlcph: add clkph ; add the old phase add clkfr ; add the phase increment (frequency) sacl clkph sub 1,14 ; if the phase is greater than two pi, we have hit a blz wait ; baud boundary, if not, go back to wait and start over ; We have crossed a baud boundary since the phase is >= 2^14. sacl clkph ; store it for next sample complex tone generation step ; ; We have had to comprimise. The most we could fit, due to constraints ; of 128 data memory data locations in page 0, is just over half the total ; bit into our delay line. HOWEVER, this is "the important half" ; It is the center and contains the "peaks" around the guaranteed ; zero crossing in the center of the bit. Convolve with the bit shape ; and get 0 or 1. ; ; de Manchester the bits in the delay line lt bd12 mpyk 1024 pac lt bd11 mpyk 4095 apac lt bd10 mpyk 4095 apac lt bd8 mpyk 1024 apac lt bd7 mpyk 128 apac lt bd5 mpyk -128 apac lt bd4 mpyk -1024 apac lt bd2 mpyk -4095 apac lt bd1 mpyk -4095 apac lt bd0 mpyk -1024 apac sach bit,4 ; Store the de-Manchestered bit ; There is an ambiguity in the clock since the clock tone recovery ; above runs at 800 Hz. We must decide which is the REAL bit center ; and then output that bit determined above. This is a statistical ; process. We are guaranteed a bit transition in center of each ; bit. If we are on the boundary, the occasional change from a zero ; to a one and vice versa causes NO transition. The process above ; Will yield a small positive number for abs(bit) when there is no ; transition lac bit larp 0 ; set auxiliary register pointer to the receive clock control. ; clock control register banz b1 ; is it bit time 0 or bit time 1 bgz psb0 ; it is bit time zero. Is the bit positive? lac mone,10 ; No load -1 sacl bit0 ; and store it in bit 0 b rar0 psb0: lac one,10 sacl bit0 rar0: lark ar0,1 ; reset the counter for the next two 2pi crossings. lac bit ; load bit for the bit time 0, take absolute value abs sacl bita0 ; store it for later b wait ; The bit time was 1 and not zero. b1: bgz psb1 ; is bit positive? lac mone,10 ; no so load -1 sacl bit1 ; store it in bit one b rar1 ; yes it is positive psb1: lac one,10 ; load one sacl bit1 ; store it in bit one rar1: lac bit ; load bit for the bit time 1, take absolute value abs sacl bita1 ; store it for later lack 13 ; load the accumulator with 1101 binary and bitpc ; use it mask the PC control word to destroy old bit. xor one ; tick the output clock sacl bitpc ; store it lac inty or one ; setup to interrupt PC with new control word sacl inty lac bita1 ; load the bit1 size sub bita0 ; sub the bit0 size add bitm0 ; integrate sacl bitm0 ; store it sub 1,12 ; let 4096 be the rails bgz th ; are we greater than 4096? lac bitm0 ; load it again add 1,12 ; add 4096 blz tl ; still less than zero? < -4096? b dbt ; Yes we are greater than 4096, so make us 4096 th: lac 1,12 sacl bitm0 b dbt ; Yes we are less than -4096, so make us -4096 tl: zac sub one,12 ; load -4096 sacl bitm0 ; store it dbt: lt bitm1 mpyk 7 ; average with 31/32 times old + 1/32 * new pac sacl cosy lac bitm1,15 lac cosy,12 add bitm0,11 sach bitm1 lac bitm1 ; sacl bito ; uncomment to see which clock phase was chosen for ; the true center of bit blz bi0 ; is the averaged size of bit0 bigger? ; NO lac bit1 ; load bit 1 b outb ; jump to modify PC control word bi0: lac bit0 outb: ; sacl bito ; uncomment to send bits out the D/A port blz wait ; if the bit is negative, bitpc is already setup lac one,1 ; if not we need to add a 1 in the 2's spot, i.e. add 2. or bitpc sacl bitpc ; store it in the PC control word outp: b wait ; go output and then do it again ; complex tone generator if modem>0 if modem<0 sine wave generator tones: sacl wkph ; store a working copy lac wkph,4 subh one blz getem ; is it in a quadrant bigger than first? subh one bgez thfr; is it in a quadrant greater than two? lac 1,13 ; nope so load pi sub wkph ; subtract phase so that it maps back into 1st quad sacl wkph ; store lac mone ; load -1 sacl mpc ; store it in the cosine multiplier b getem ; go read tables thfr: lac mone ; multiplier for bottom half sacl mps ; store lac wkph ; sub one,13 ; map angle back to upper half and go do it again b tones getem: lac wkph,10 ; take 1st quadrant phase equiv for sine and pick ; off coarse part of phase address sach coph ; store it lack sintbl ; load sine table offset into accumulator add coph ; add coarse address off set tblr sinx ; read the coarse sine value lac one,6 ; load pi/2 sub coph ; subtract the coarse phase to get cosines offset sacl coph lack sintbl add coph ; do same as above for coarse cosine tblr cosx lac wkph ; load working phase and maskl ; mask off fine addres sacl coph ; store it lack fines ; locate fine table offset add coph ; add for offset into fine table tblr siny ; read table lack finec add coph tblr cosy zac lt sinx ; load sinx mpy cosy ; multiply by cosy lta siny ; load t reg with fine sin and accumulate previous prod. mpy cosx ; multiply by coarse cosx to use sin(X+Y) apac ; add the result to coarse sine sach sine ; store it. lac modem blz mult lac 1,12 ; load full address pi/2 sub wkph ; subtract the working phase to get cosine sacl wkph lac wkph,10 ; from here to the later MARK it is identical to above sach coph lack sintbl add coph tblr sinx lac one,6 sub coph sacl coph lack sintbl add coph tblr cosx lac wkph and maskl sacl coph lack fines add coph tblr siny lack finec add coph tblr cosy zac lt cosy mpy sinx lta siny mpy cosx apac ; MARK sach cosine ; store it in the cosine mult: lt mps; now we need to do a few multiplies by sign changes due to mpy sine ; to quadrant part of phase address pac; multiply sine by sine sign (:-) and sacl sine ; store the result lac modem bgz cosm ret cosm: mpy cosine; now multiply cosine by the same pac sacl cosine ; store it lt mpc; load cosine differentiator from sine sign (:-) mpy cosine; multiply pac sacl cosine ; store ret end ; Ebbly Ebbly Ebbly thats all folks