## Sunday, June 5, 2016

### Summertime! Let's get out there and fox hunt! (Part 2)

In my previous post, I described the first half of a hidden transmitter (fox) implemented using a Propeller Mini board for an HF CW beacon and some simple firmware.  In this installment, I will describe the 2 meter FM portion of the firmware.

My first thought was to implement the VHF beacon transmitter using the same design as used on the HF CW beacon except to use MCW rather than CW using FM modulation of the 2 meter carrier.

This approach is simple enough, but rather than generate the audio CW signal to FM modulate the carrier, why not just modulate it with any recorded audio?  In this way a vocal announcement could be use to identify the beacon.

In my case I decided to use a free Windows application called "Audacity" to make an audio recording of a 2 second announcement "Salmoncon Fox Hunt" and save it as a .wav file.  The file is exported from Audacity as a 11.025 kHz sample rate, 8 bit, monaural recording which is then loaded into the program flash of the Propeller chip and used to FM modulate the 146 MHz carrier.  At 11.025 kHz, two seconds of audio will be 11025 * 2 bytes plus 44 bytes of wav file header.

Using 8 bit samples, the sample byte is multiplied by 32 and added to the frequency being generated.  SInce this deviation of the carrier frequency is happening at an audio rate, we have achieved FM modulation of the transmitter.  This yields a maximum of 256 * 32 = 8.192 kHz of frequency deviation.  A multiplier of 58 will get you to just shy of 15 kHz.  For my purposes, 8 kHz is just fine.

Let's take a quick walk through the code as it is pretty trivial.  Up top we see the same structure defining the necessary constants used in the code.  In the comments we can see the formula for calculating the value used to set the RF frequency.  For example:

146520000 / (16 * 80000000) * 2^32 = 491639537.664.  Truncating to an integer is sufficient for this purpose.  This value is loaded into the counter FRQA register to generate the desired 146.52 MHz signal.

' 2 meter FM fox hunt beacon
'
' ko7m - Jeff Whitlatch
'
' FRQx = frequency / (16 * CLKFREQ) * 2^32
'
' Propeller manual recommends limiting upper frequency to 128 MHz for best stabillity
' We of course ignore such advice and utilize it at 146.52 MHz

CON

_clkmode   = XTAL1 + PLL16X
_xinfreq   = 5_000_000

WMin       = 381
RFPin      = 8

FRQAvalue  = 491_639_537      ' For 146.52 MHz (146520000 / (16 * 80000000) * 4294967296)
sampleRate = 11025            ' 11.025 kHz audio
waitKeyDn  = 1000             ' Delay before audio starts with key down
waitKeyUp  = 2000             ' Delay after audio ends before key up
wavHdrSize = 44               ' Size of wave file header
devScale   = 5                ' deviation scaler for audio samples (256 * 32 = 8.192 kHz)

The only local variables used are when we are running this code on its own core.  In this case we define the Stack space required and keep track of which core (cog) was assigned.

VAR
LONG Stack[16]
BYTE Cog

The Main function initializes the VHF beacon and runs the main function.  To enable this code to be run on separate cores, Start and Stop functions are provided.

PUB Main
doInit
doBeacon

PUB Start : fSuccess
fSuccess := (Cog := cognew(Main, @Stack) + 1) > 0

PUB Stop
if Cog
cogstop(Cog~ - 1)

The doInit function sets up the RF pin as an output and configures the counter to enable RF generation on 2 metres

pri doInit
DIRA[RFPin]~~                                         ' Set RF pin as an output
CTRA := constant(010_111 << 23) + RFPin            ' Configure the counter

This is the main function of the beacon.  The CPU clocks per sample of audio at the audio sample rate is calculated.  The index of the first byte of audio is calculated and RF generation of a carrier is initiated.  After a brief pause, each byte of the audio data is used to FM modulate the carrier.  Once the entire message is sent, the carrier is kept on for a couple seconds to help fox hunters zero in on the transmitter.  The RF is then shut down and after a short delay, the process repeats.

PRI doBeacon | p, time, clocksPerSample

clocksPerSample := clkfreq / sampleRate               ' System clocks per audio sample

p := constant(@audioStart + wavHdrSize)       ' Pointer into our audio data just past the wav file header

FRQA := FRQAvalue                                     ' Go key down
delay(waitKeyDn)                                      ' Wait before modulation starts

time := cnt

' Play the audio file once through
repeat while p++ < @audioEnd
FRQA := FRQAvalue + byte[p] << devScale               ' Scale amplitude byte by 32 (2^8 * 32 = 8.192 kHz deviation max)
waitcnt(time += clocksPerSample)                    ' Wait until time for the next sample

delay(waitKeyUp)                                      ' Delay before dropping carrier

FRQA := 0                                             ' Turn off NCO

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

The audio data is stored in the data secion of the code.  Since there is only 64 kb of ROM on the device, the amount of recorded data is limited to available ROM space.  If desired, the audio sample rate could be reduced to 8 kB to enable increased message length.  The actual audio data is stored in the "foxhunt.wav" file specified in the same directory as this file.

DAT

audioStart byte
File "foxhunt.wav"                                      ' Audio data to be transmitted

audioEnd byte 0

Ok, so now we have two beacons that can be used independently.  In my next installment, I shall tie these two modules together so they can be used simultaneously.  I will also provide a hex file of the compiled code should you not wish to mess with installing the Propeller Tools for code development.

Improvements of course could be made.  Any sample rate of audio could be used and the encoded rate read from the .wav file header for example rather than needing to change a constant for a different sample rate file.  Let your imagination be your guide.  Have fun and as always if you get stuck, drop me a note at ko7m at arrl dot net and I will try my best to help you out.

See you in the next installment.  For your convenience, the entire source code is reproduced below.

' 2 meter FM fox hunt beacon
'
' ko7m - Jeff Whitlatch
'
' FRQx = frequency / (16 * CLKFREQ) * 2^32
'
' Propeller manual recommends limiting upper frequency to 128 MHz for best stabillity
' We of course ignore such advice and utilize it at 146.52 MHz

CON

_clkmode   = XTAL1 + PLL16X
_xinfreq   = 5_000_000

WMin       = 381
RFPin      = 8

FRQAvalue  = 491_639_537      ' For 146.52 MHz (146520000 / (16 * 80000000) * 4294967296)
sampleRate = 11025            ' 11.025 kHz audio
waitKeyDn  = 1000             ' Delay before audio starts with key down
waitKeyUp  = 2000             ' Delay after audio ends before key up
wavHdrSize = 44               ' Size of wave file header
devScale   = 5                ' deviation scaler for audio samples (256 * 32 = 8.192 kHz)

VAR
LONG Stack[16]
BYTE Cog

PUB Main
doInit
doBeacon

PUB Start : fSuccess
fSuccess := (Cog := cognew(Main, @Stack) + 1) > 0

PUB Stop
if Cog
cogstop(Cog~ - 1)

pri doInit
DIRA[RFPin]~~                                         ' Set RF pin as an output
CTRA := constant(010_111 << 23) + RFPin            ' Configure the counter

PRI doBeacon | p, time, clocksPerSample

clocksPerSample := clkfreq / sampleRate               ' System clocks per audio sample

p := constant(@audioStart + wavHdrSize)               ' Pointer into our audio data just past the wav file header

FRQA := FRQAvalue                                     ' Go key down
delay(waitKeyDn)                                      ' Wait before modulation starts

time := cnt

' Play the audio file once through
repeat while p++ < @audioEnd
FRQA := FRQAvalue + byte[p] << devScale               ' Scale amplitude byte by 32 (2^8 * 32 = 8.192 kHz deviation max)
waitcnt(time += clocksPerSample)                    ' Wait until time for the next sample

delay(waitKeyUp)                                      ' Delay before dropping carrier

FRQA := 0                                             ' Turn off NCO

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

DAT

audioStart byte
File "foxhunt.wav"                                      ' Audio data to be transmitted

audioEnd byte 0