; 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
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 <TX> 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
; 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.
; <RX> 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