## Tuesday, January 31, 2012

### Randomizing WSPR TX Percent

My propeller WSPR beacon so far has implemented TX Percent as a constant.  For example if you set TXPercent to 20% (0.20) then it will transmit every 10 minutes.  Joe Taylor's WSPR specification states that in reality, it should transmit randomly within that interval in order to minimize collisions with other stations transmitting with the same TX Percent value.  So rather than transmitting exactly every 10 minutes with this TX Percent value, I have implemented a routine to pick a random 2 minute period up to the value specified by TX Percent.  So, TX Percent now effectively specifies the maximum amount of time to wait between transmissions.

I created a variable to hold the current random number and initialized it with a constant.  The value of the constant is not important.  Pick your birthday or whatever.  It becomes the starting seed for the random number generator.

VAR
LONG Rnd

PUB doInitialize
Rnd  := 22041956                     ' Initialize random number seed

I then created a new procedure to calculate how long to wait before the next transmission based on TX Percent.  TX Percent is declared as a constant.  For me it is set to 20%

CON
TXPercent =  0.20     ' Transmit 20% of the time or about every 10 minutes

PUB nextSync : s
s := ROUND(1.0 / TXPercent * 2.0)    ' Calculate when next xmit occurs in minutes
s := (||?Rnd // s)                   ' Randomize the result
s += (s // 2)                        ' Send on a 2 minute boundary
s *= 60                              ' Convert minutes to seconds

Right.  Now, in the main loop, we calculate how long to wait with each go by calling nextSync and setting the new value of Sync to the returned value.

PUB Main
doInitialize
repeat
'Send symbol set for KO7M CN87 7
sendCode(@mySyms)
noTone
Sync := nextSync
repeat while SecondCnt // Sync
delay(100)

This seems to do the job nicely.  While it is not always the case that you may want to have the transmission start time randomized, this brings the beacon code closer to that of the WSPR 2.x specification.  If you wish to have constant transmission periods, just change the Sync := nextSync line to set Sync to a constant number of seconds between transmissions.  Remember to make sure this equates to an even number of minutes between transmissions.

As you can see, my transmissions are now being nicely randomized up to 10 minutes.

For those interested, here is the entire updated beacon code:

CON
_CLKMODE = XTAL1 + PLL16X
_XINFREQ = 5_000_000
RFPin        =         27        ' I use pin 27 for RF output

' WSPR standard frequencies

' Shown are the dial frequencies plus 1500 hz to put in the middle of
' the 200 hz WSPR band
WSPR500KHz   =     502_400 + 1_500    ' 500 KHz
WSPR160M     =   1_836_600 + 1_500    ' 160 metres
WSPR80M      =   3_592_600 + 1_500    '  80 metres
WSPR60M      =   5_287_200 + 1_500    '  60 metres
WSPR40M      =   7_038_600 + 1_500    '  40 metres
WSPR30M      =  10_138_700 + 1_500    '  30 metres
WSPR20M      =  14_095_600 + 1_500    '  20 metres
WSPR17M      =  18_104_600 + 1_500    '  17 metres
WSPR15M      =  21_094_600 + 1_500    '  15 metres
WSPR12M      =  24_924_600 + 1_500    '  12 metres
WSPR10M      =  28_124_600 + 1_500    '  10 metres
WSPR6M       =  50_293_000 + 1_500    '   6 metres
WSPR4M       =  70_028_600 + 1_500    '   4 metres
WSPR2M       = 144_489_000 + 1_500    '   2 metres

Frequency    =     WSPR30M     ' Set to 30 metres
ErrorOffset  =          20     ' Specific to my propeller board - change this
symbolLength =         683     ' 8192 / 12000 * 1000 = milliseconds
TXPercent    =        0.20     ' Transmit 20% or about every 10 minutes

OBJ
Freq : "Synth"
'WSPR : "ko7mWSPREncode"
'T    : "PSM_tv_text"

VAR
LONG SecondCnt, Stack[16]
LONG Sync
LONG Rnd

DAT
' Symbol set for KO7M CN87 7
mySyms BYTE "33020202322033300230030333100222201023012020023033"
BYTE "22132320011212000112303210300300303300031212302010"
BYTE "22021203023312132013030201132002230302330002020310"
BYTE "303320231002", 0

PUB Main
doInitialize
repeat
'Send symbol set for KO7M CN87 7
sendCode(@mySyms)
noTone
Sync := nextSync
repeat while SecondCnt // Sync
delay(100)

PUB doInitialize
Rnd  := 22041956                    ' Initialize random seed
cognew(Clock, @Stack)               ' Start the clock COG

PUB sendCode(stringptr)
if TXPercent > 0      ' If zero, not transmitting
repeat strsize(stringptr)
sendSymbol(byte[stringptr++])

PUB sendSymbol(char)
case char
"0", 0:
sendTone(-3)
delay(symbolLength)
"1", 1:
sendTone(-1)
delay(symbolLength)
"2", 2:
sendTone(1)
delay(symbolLength)
"3", 3:
sendTone(3)
delay(SymbolLength)

PUB sendTone(tone)
Freq.Synth("A",RFPin, Frequency + tone + ErrorOffset)

PUB noTone
Freq.Synth("A",RFPin, 0)

PUB delay(Duration)
waitcnt(((clkfreq / 1_000 * Duration - 3932) #> WMin) + cnt)

PUB Clock              ' Runs In its own COG
repeat
delay(1000)        ' Update second counter every 1000 ms
SecondCnt++        ' Should be good for 2^32 seconds or about 136 years

PUB nextSync : s
s := ROUND(1.0 / TXPercent * 2.0)    ' Calculate when next TX occurs in minutes
s := (||?Rnd // s)                   ' Randomize the result
s += (s // 2)                        ' Always send on a 2 minute boundary
s *= 60                              ' Convert minutes to seconds