; This is a modem for Coast Guard DGPS transmissions. ; It runs on the DSP56002 EVM card with the Leonid monitor. ; The input is a SSB signal, with the carrier at 1KHz. ; The tuning needs to be within 5 or 10Hz. ; USB and LSB will both work. The output is 4800 baud ; RTCM data stream on the SCI port. You can plug this directly ; into your GPS receiver. It works fine with my Garmin 45 and ; GPS II+ receivers, and the Sandy Hook, NJ beacon at 286 KHz. ; ; The Orange LED lights up to indicate RTCM word sync, ; and blinks if there is a RTCM parity error. ; ; This is hard-coded for the 200BPS input data rate and 4800 baud ; output data rate. You will have to mess with filter bandwidths, ; etc., to make it work for 100BPS transmissions. ; ; Put together by Doug Braun, N1OWU (dougbert@rcn.com). Many ; bits and pieces have been stolen from other EVM programs. ; ; TODO: ; The filter bandwidths are not necessarily optimum. ; The DCD algorithm is poor (but this does not affect the ; data output, only the DCD LED). ; Activate AutoTune-tune threshold detection to allow better reception of ; out-of-tune signals. ; ; Wish List: ; Auto detection and handling of 100 and 200 BPS trnasmissions. ; ; ; $Header: E:/HAM/EVM/RCS/dgps.asm 1.16 1998/11/23 09:11:24 dbraun Exp $ ; ; $Log: dgps.asm $ ; Revision 1.16 1998/11/23 09:11:24 dbraun ; Added notes. ; ; Revision 1.15 1998/11/23 08:55:02 dbraun ; Added snazzy amplitude-independent phase detection algorithm. ; ; Revision 1.14 1998/11/23 08:15:43 dbraun ; Perfected parity algorithm ; ; Revision 1.13 1998/11/22 19:16:01 dbraun ; Parity algorithm finally works. ; ; Revision 1.12 1998/11/20 13:50:45 dbraun ; More work on RTCM parity ; ; Revision 1.11 1998/11/20 10:03:36 dbraun ; Ongoing work ; ; Revision 1.10 1998/11/17 21:38:10 dbraun ; Scaled Bit Filter coess to avoid possibility of overflow. ; ; Revision 1.9 1998/11/17 18:02:58 dbraun ; Minor tweaks an dcleanup ; ; Revision 1.8 1998/11/16 21:07:09 dbraun ; Fixed a mistake in previous revision. ; ; Revision 1.7 1998/11/16 20:48:02 dbraun ; Removed Rx processing queue, which seems unnecessary for a ; non-packetized (streaming) application like DGPS. ; ; Revision 1.6 1998/11/15 21:38:06 dbraun ; Increased filter BW from +-80 to +-100 Hz and fixed small bug ; inherited from original code. ; ; Revision 1.5 1998/11/15 20:12:59 dbraun ; Added Raw output option ; ; Revision 1.4 1998/11/13 09:36:20 dbraun ; Rearranged memory usage ; ; Revision 1.3 1998/11/13 08:32:11 dbraun ; Got DCD to work reasonably well ; ; Revision 1.2 1998/11/13 03:46:26 dbraun ; Seems to work ; ; Revision 1.1 1998/11/12 22:11:31 dbraun ; Initial revision ; ;Based on: ;FSK modem for 1200 bps (1200Hz and 2200Hz tones) ; Version: E:/HAM/EVM/RCS/fsk1200.asm 1.5 1997/10/23 13:40:14 dbraun Exp ;(c) 1995 Pawel Jalocha, SP9VRC ;Free license is given for radio-amateur usage _only_. ;This version of 21st July 1995 opt mu nolist include 'leonid' include 'ioequlc' list title 'DGPS Modem' DCDLED macro mode ; DCD LED clr/set/chg b\mode #13,X:$FFE4 endm UpLED macro mode ;UP LED b\mode #1,X:$FFE4 endm DownLED macro mode ;DOWN LED b\mode #2,X:$FFE4 endm YellowLED macro mode ;CAT line (a yellow LED connected) b\mode #3,X:$FFE4 endm OrangeLED macro mode ; (an orange LED connected) b\mode #14,X:$FFE4 endm SampleFreq equ 9600.0 BufLen equ 64 ;sample buffer length BatchLen equ 8 ;processing batch length (must be > 1) BatchLenLog equ 3 ;BatchLen = 2 ^ BatchLenLog DCrise equ 256 ;DC rise time in samples ;the larger, the slower the DC follows RMSrise equ 256 ;RMS rise time in samples ;the larger, the slower the RMS follows AGCenable equ 1 ;automatic adjustment of the input amplification AGCalert equ 1 ;indicate too low or too high audio levels with the LEDs AGChold equ 512 ;AGC hold time in samples AGCfall equ 128 ;AGC fall time in samples/AGC unit (1.5dB) - default if AGCenable RMSmin equ 0.050 ;minimum and maximum RMS values for the front-end AGC else RMSmin equ 0.010 ;without AGC enabled, the AGCmin and max values serve ;only to determine when to flash the LEDs. endif RMSmax equ 0.200 ;do not make these closer than by a factor of 2 ;the RMSes are indeed MSes that without the square root BitFreq equ 2*200.0/SampleFreq ;200 bits per second CarFreq equ 2*1000.0/SampleFreq ;center freq. = 1000 Hz CarDev equ 2*50.0/SampleFreq ;deviation = +/- 50 Hz BitLen equ @cvi(2.0/BitFreq) ;bit-length in samples RawOutput equ 0 ; If set, we get raw 200 baud demodulator output OutputBaudRate equ 4800 ; RTCM output baudrate (if RawOutput is not set) RxLevelFollow equ 1.0/32 ;Rx Levels and DCD tracking speed (counts per bit) RxDCDindicate equ 1 ;indicate DCD status with the red LED RxClockFollow equ 1.0/4096 ;Rx bit-clock recovery speed (counts per sample) ;too much is too good... SPY equ 0 ;for spying with SPY.EXE (disables the KISS protocol) LowFreq = (1000.0-100.0)/SampleFreq ;receive/transmit filters edges (-6dB) UppFreq = (1000.0+100.0)/SampleFreq PassFilterLen equ 256 ;receive/transmit filters length ;longer length make sharper filters ;but introduces longer delays BitFilterLen equ 2*BitLen HardLimiter equ 0 ;hard limit the signal after the filter ;and then filter the signal again ;before FM demodulator ;if this is present the InternalAGC ;makes little sense. InternalAGC equ 1 ;somehow helps (or substitutes) the CODEC-level AGC. AutoTune equ 0 ;bit threshold automatic adjustment ;This seems unnecessary for a properly-tuned DGPS ;receiver, which should always be tuned within 5Hz ;or so. RxGain equ 3.0 ;Line In (initial, if AGC enabled) gain ;************************************************************************* ;The actual code (internal/external program RAM) LOMEM P:$0000 HIMEM P:$1FFF org p:user_code andi #%11110011,mr ;scaling bits = 00 move #$FFFF,m1 ;r1 to address the coeff. of all filters ;or any other linear structures move #Buffer,r2 ;for us to address the input and output samples move #<4-1,n2 move #1 .loop #BatchLen-1 ;average samples with the batch add x0,a X:(r2)+n2,x0 add x0,b X:(r2)+,x0 .endl endif add x0,a X:(r2)+n2,x0 add x0,b move x1,r2 ;restore r2 if BatchLenLog>0 .loop #BatchLenLog ;scale the average asr a asr b .endl ;now: a = left DC, b = right DC endif rnd a #(1.0-1.0/DCrise*BatchLen),y0 ;further DC filter rnd b #1.0/DCrise*BatchLen,y1 move Y:1 .loop #BatchLen-1 ;sum up the signal squares mac x0,x0,a X:(r2)+n2,x0 mac x0,x0,b X:(r2)+,x0 .endl endif mac x0,x0,a X:(r2)+n2,x0 mac x0,x0,b move x1,r2 ;restore r2 if BatchLenLog>0 .loop #BatchLenLog ;divide by BatchLen asr a asr b .endl ;now: a = left RMS, b = right RMS endif rnd a #(1.0-1.0/RMSrise*BatchLen),y0 ; filter these RMSes to get smoother rise/fall rnd b #1.0/RMSrise*BatchLen,y1 move X:BatchLen,x0 sub x0,a #>AGCfall,x0 move a,X:$0F0F00,x0 and x0,a ;extract the gain cmp x0,a #>$010100,x0 ;already maximum ? jeq $F0F000,x0 ;if not, increment the gain by 1 move a1,x1 move Y:(r2),a1 ;and reload all the control words and x0,a n2,x0 ;in the output buffer or x1,a #<4,n2 ;make n2=4 for a moment .loop #BufLen move a1,Y:(r2)+n2 .endl move x0,n2 ;restore n2 move #0.7071,y0 ;increase the RMSes to follow the gain move X:RMSleft,x0 ;increase faster mpyr x0,y0,a asl a X:AGChold,x0 ;initialize the AGC hold count-down move x0,X:$0F0F00,x0 and x0,a #>$010100,x0 ;extract the gain bits sub x0,a #>$F0F000,x0 ;attempt to decrease the gain jcs positive transition jgt $000020,x0 cmp x1,b ;eye open ? jge $000010,x0 jge YellowLED clr .else YellowLED set .endi rts procbit btst #23,X: bset #1,X:m_pcd .else bclr #1,X:m_pcd .endi endif else ; send as part of a RTCM byte move X: ; bit count has reached zero, so reset the counter ; and send out a data byte move #>6,x0 move x0,X: bset #0,a0 .endi bclr #8,a1 ;Keep bits 32-47 clear move a1,X: ; bit count has reached zero, so reset the counter move #>30,x0 move x0,X: ; Good parity! inc b clr a move #>MaxScore,a0 cmp a,b .if move #>MaxScore,b0 bset #0,X: clr b bclr #0,X:1,x0 move x0,X: andi #$fb,ccr ;clear zero flag rts .endi move L: andi #$fb,ccr ;clear zero flag rts .endi ; Second, complement the data bits if the previous ; word's LS parity bit is set. btst #6,a1 .if move L:$3f,x1 ;mask the parity error bits and x1,a1 ;Z flag is set rts ; This checks the simple parity of the 32 LS bits of A, and returns it ; in the LSB of B0. A is modified. The other bits of B0 are not changed. ParityA bclr #0,b0 .loop #32 asr a .if bchg #0,b0 .endi nop ;Necessary to avoid nasty illegal situation of jump to .endl .endl rts PI equ 3.14159265358979323846 EX equ 2.718281745911 ;this routine computes a cosine/sine pair using the sine ROM ;with a second order (linear+quadrature) approximation between table points IQ ;x0 = angle ( -1 = -PI, +1 = +PI) move #>$80,x1 ;shift out 8 most significant bits mpy x0,x1,a #>$FF,x0 ori #%00000011,mr ;disable interrupts move x0,m0 and x0,a #>$100,x0 or x0,a #<$40,n0 ori #%00000100,omr ;enable the sine ROM table move a1,r0 ;put the 8 most significant bits into r0 with offset = $100 move a0,y0 ;save the remaining bits in y0 jclr #23,y0,SinTable move (r0)+ SinTable move Y:(r0+n0),x0 ;x0 = coarse cosine move Y:(r0),x1 ;x1 = coarse sine mpyr x1,y0,a #PI/256.0,y1 tfr x0,a a,x1 macr -x1,y1,a ;a = fine cosine mpyr x0,y0,b Y:(r0),x1 andi #%11111011,omr ;disable the sine ROM table tfr x1,b b,x1 macr x1,y1,b #PI*PI/2.0/65536.0,y1 ;b = fine sine mpyr y0,y0,a a,x0 andi #%11111100,mr ;enable interrupts move a,y0 mpyr y0,y1,a tfr x0,a a,y1 macr -x0,y1,a b,x1 ;a = super fine cosine macr -x1,y1,b ;b = super fine sine rts ;x,y are modified ;r0,m0,n0 are modified ;maximum error is about 0.7E-6 ;execution time 4+64+4 clock cycles ;including "jsr turn by PI .if neg a #<%10000000,x0 neg b .endi tst a ;if I-part negative => turn by -PI/2 .if bset #22,x0 tfr a,b b,a ;swap I with Q neg b ;negate the new Q .endi add b,a ;add Q to I (what if I+Q >= 1.0 ?) asr a ;I+Q /= 2 in case it is > 1 asr b a,x1 ;Q /= 2 because of the above, x1 = I+Q andi #$FE,ccr ;divive Q by (I+Q) rep #24 div x1,b tfr x0,a b0,x0 ;x0 = Q div (I+Q), a=approx angle tfr x0,b #0.5,x0 ;b = Q/(I+Q) sub x0,b #0.25,x0 ;b = Q/(I+Q) - 1/2 =: X add x0,a b,x1 #0.63361654,y1 ;angle += 0.25, x1 = X mac x1,y1,a x1,x0 ;angle += F1*X, x0 = X mpyr x0,x0,b #-0.73728217,y1 ;b = X*X mpyr x1,y1,b b,x0 ;b = F3*X, x0 = X*X move b,y1 mac x0,y1,a x0,y0 ;angle += F3*X * X*X, y0 = X*X mpyr x0,x0,b #0.85568791,y1 ;b = (X*X)*(X*X) mpyr x1,y1,b b,x0 ;b = F5*X, x0 = X*X*X*X move b,y1 mac x0,y1,a ;angle += F5*X * X*X*X*X mpyr x0,y0,b #-0.17769569,y1 ;b = (X*X*X*X)*(X*X) mpyr x1,y1,b b,x0 ;b = F5*X, x0 = X*X*X*X*X*X move b,y0 macr x0,y0,a ;angle += F5*X * X*X*X*X*X*X rts ;a = the phase (-1.0 => -PI, 1.0 => PI) ;b,x,y are modified ;execution time is about 130 cycles (65 instructions) if SPY SpySync move a10,L: no spy request move a2,X: spy request ! move x0,Y:'S',a cmp x0,a ori #$01,ccr jne 'P',x0 putc move #>0,x0 ; send a 0 meaning non-complex data putc move #>512,a move a,X:'S',a cmp x0,a jne 'P',x0 putc move #>0,x0 ; send a 0 meaning non-complex data putc move #>512,a Spy_copy move #>1,x0 sub x0,a move a,X:=@cvi(LastY) org L:LastX else org L:LastY endif ; Make sure this is above last P/X/Y memory location Buffer dsm BufLen*4 ;CODEC's input/output buffer LastL = * end ;*************************************************************************