Generates RTTY from a GPS receiver

Dependencies:   mbed-dsp mbed-rtos mbed

Committer:
adwiens
Date:
Sat Jan 11 01:55:42 2014 +0000
Revision:
0:dbb85bfd22fd
Child:
1:07d7070e9252
First commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
adwiens 0:dbb85bfd22fd 1 #include "mbed.h"
adwiens 0:dbb85bfd22fd 2 #include "rtos.h" // mbed real time os library
adwiens 0:dbb85bfd22fd 3 #include "dsp.h" // mbed digital signal processing library
adwiens 0:dbb85bfd22fd 4 #include <cstring>
adwiens 0:dbb85bfd22fd 5 #include <string>
adwiens 0:dbb85bfd22fd 6 #include <iostream>
adwiens 0:dbb85bfd22fd 7 #include <sstream>
adwiens 0:dbb85bfd22fd 8 #include <map>
adwiens 0:dbb85bfd22fd 9
adwiens 0:dbb85bfd22fd 10 #define GPS_CB_SIZE (16) /* size of gps circular buffer in characters */
adwiens 0:dbb85bfd22fd 11 #define GPS_BAUD (9600) /* gps serial port speed in bps */
adwiens 0:dbb85bfd22fd 12 #define RADIO_TX_WAIT (2000) /* time between radio transmissions in ms */
adwiens 0:dbb85bfd22fd 13 #define RADIO_KEYUP_DELAY (1000) /* time to wait for radio transmitter to turn on */
adwiens 0:dbb85bfd22fd 14 #define AUDIO_FS (22050) /* audio sample rate in hz */
adwiens 0:dbb85bfd22fd 15 #define RTTY_BAUD (45.45) /* rtty bit rate in bps */
adwiens 0:dbb85bfd22fd 16 #define MARK_FREQ (2125) /* mark frequency (1) in hz */
adwiens 0:dbb85bfd22fd 17 #define SPACE_FREQ (2295) /* space frequency (0) in hz */
adwiens 0:dbb85bfd22fd 18 #define AUDIO_VOL (0.25) /* range 0-1 */
adwiens 0:dbb85bfd22fd 19
adwiens 0:dbb85bfd22fd 20 using namespace std;
adwiens 0:dbb85bfd22fd 21
adwiens 0:dbb85bfd22fd 22 Serial pc(USBTX, USBRX); // pc serial port (via usb)
adwiens 0:dbb85bfd22fd 23
adwiens 0:dbb85bfd22fd 24 // GPS variables:
adwiens 0:dbb85bfd22fd 25 char cb[GPS_CB_SIZE]; // c-string circular buffer for gps rx isr
adwiens 0:dbb85bfd22fd 26 int cb_isr_i = 0; // isr index
adwiens 0:dbb85bfd22fd 27 int cb_thr_i = 0; // thread index
adwiens 0:dbb85bfd22fd 28 DigitalOut gps_led(LED1); // gps status led
adwiens 0:dbb85bfd22fd 29 Serial gps(p9, p10); // gps serial port (uart3)
adwiens 0:dbb85bfd22fd 30 stringstream rxbuf; // gps receive buffer
adwiens 0:dbb85bfd22fd 31 map<string,string> nmea_data; // most recent nmea sentences
adwiens 0:dbb85bfd22fd 32 Mutex nmea_data_mutex; // nmea data lock
adwiens 0:dbb85bfd22fd 33
adwiens 0:dbb85bfd22fd 34 // RTTY variables:
adwiens 0:dbb85bfd22fd 35 AnalogOut dac(p18); // mbed built-in digital to analog converter
adwiens 0:dbb85bfd22fd 36 DigitalOut ptt(p17); // radio push to talk button
adwiens 0:dbb85bfd22fd 37 DigitalOut tx_led(LED2); // tx status led
adwiens 0:dbb85bfd22fd 38 stringstream txbuf; // rtty tx buffer
adwiens 0:dbb85bfd22fd 39 string txbufstr;
adwiens 0:dbb85bfd22fd 40 const char *txchar = NULL; // current character to transmit
adwiens 0:dbb85bfd22fd 41 float angle = 0.0; // current sine angle
adwiens 0:dbb85bfd22fd 42 int ifreq = MARK_FREQ; // instantaneous frequency
adwiens 0:dbb85bfd22fd 43 int bitn = -1; // current bit number
adwiens 0:dbb85bfd22fd 44 bool txen = false; // tx enable flag
adwiens 0:dbb85bfd22fd 45
adwiens 0:dbb85bfd22fd 46 // This function is the interrupt service routine for the gps.
adwiens 0:dbb85bfd22fd 47 // It is called when the serial port receives a character,
adwiens 0:dbb85bfd22fd 48 // and it puts the character into a circular buffer for the gps thread.
adwiens 0:dbb85bfd22fd 49 void gps_rx_isr()
adwiens 0:dbb85bfd22fd 50 {
adwiens 0:dbb85bfd22fd 51 cb[cb_isr_i] = LPC_UART3->RBR; // avoid mutex lockup (https://mbed.org/forum/bugs-suggestions/topic/4217/)
adwiens 0:dbb85bfd22fd 52 if(++cb_isr_i >= GPS_CB_SIZE) cb_isr_i = 0; // loop circular buffer index
adwiens 0:dbb85bfd22fd 53 }
adwiens 0:dbb85bfd22fd 54
adwiens 0:dbb85bfd22fd 55 // This function reads new characters from the gps circular
adwiens 0:dbb85bfd22fd 56 // buffer when the circular buffer is about 25 percent full
adwiens 0:dbb85bfd22fd 57 // and adds the new characters to a string containing the current
adwiens 0:dbb85bfd22fd 58 // nmea sentence. Each time a complete NMEA sentence is received
adwiens 0:dbb85bfd22fd 59 // this function updates a map containing K,V pairs. (The sentence
adwiens 0:dbb85bfd22fd 60 // type is the key and the value is the NMEA sentence.)
adwiens 0:dbb85bfd22fd 61 void gps_rx_thread(void const *argument)
adwiens 0:dbb85bfd22fd 62 {
adwiens 0:dbb85bfd22fd 63 while(1) {
adwiens 0:dbb85bfd22fd 64 gps_led = 0;
adwiens 0:dbb85bfd22fd 65 Thread::wait((GPS_CB_SIZE)*1000*8/4/GPS_BAUD); // wait until cb 25% full
adwiens 0:dbb85bfd22fd 66 gps_led = 1;
adwiens 0:dbb85bfd22fd 67 while(cb_thr_i != cb_isr_i) {
adwiens 0:dbb85bfd22fd 68 char c = cb[cb_thr_i];
adwiens 0:dbb85bfd22fd 69 rxbuf << c; // add to string buffer
adwiens 0:dbb85bfd22fd 70 if(c == '\n') { // clear string buffer
adwiens 0:dbb85bfd22fd 71 const char *c = rxbuf.str().c_str();
adwiens 0:dbb85bfd22fd 72 const char *l = strchr(c, '$'), *r = strchr(c, ',');
adwiens 0:dbb85bfd22fd 73 if(l != NULL && r != NULL && r > l) { // check valid limits
adwiens 0:dbb85bfd22fd 74 char ctype[6];
adwiens 0:dbb85bfd22fd 75 memcpy(ctype,l+1,5);
adwiens 0:dbb85bfd22fd 76 ctype[5] = 0;
adwiens 0:dbb85bfd22fd 77 string type = ctype;
adwiens 0:dbb85bfd22fd 78 nmea_data_mutex.lock();
adwiens 0:dbb85bfd22fd 79 nmea_data[type] = rxbuf.str(); // update map
adwiens 0:dbb85bfd22fd 80 nmea_data_mutex.unlock();
adwiens 0:dbb85bfd22fd 81 }
adwiens 0:dbb85bfd22fd 82 rxbuf.str("");
adwiens 0:dbb85bfd22fd 83 rxbuf.clear();
adwiens 0:dbb85bfd22fd 84 }
adwiens 0:dbb85bfd22fd 85 if(++cb_thr_i >= GPS_CB_SIZE) cb_thr_i = 0; // loop circular buffer index
adwiens 0:dbb85bfd22fd 86 }
adwiens 0:dbb85bfd22fd 87 }
adwiens 0:dbb85bfd22fd 88 }
adwiens 0:dbb85bfd22fd 89
adwiens 0:dbb85bfd22fd 90 // This function writes individual audio samples to the dac.
adwiens 0:dbb85bfd22fd 91 void rtty_sample_tick()
adwiens 0:dbb85bfd22fd 92 {
adwiens 0:dbb85bfd22fd 93 if(txen)
adwiens 0:dbb85bfd22fd 94 {
adwiens 0:dbb85bfd22fd 95 angle += 2 * PI * ifreq / AUDIO_FS;
adwiens 0:dbb85bfd22fd 96 if(angle > 2 * PI) angle -= 2*PI;
adwiens 0:dbb85bfd22fd 97 dac = (arm_sin_f32(angle) + 1.0) / 2.0 * AUDIO_VOL; // write sample to dac
adwiens 0:dbb85bfd22fd 98 }
adwiens 0:dbb85bfd22fd 99 else
adwiens 0:dbb85bfd22fd 100 {
adwiens 0:dbb85bfd22fd 101 dac = 0;
adwiens 0:dbb85bfd22fd 102 }
adwiens 0:dbb85bfd22fd 103 }
adwiens 0:dbb85bfd22fd 104
adwiens 0:dbb85bfd22fd 105 // This function controls whether the current rtty bit is a mark or a space.
adwiens 0:dbb85bfd22fd 106 // Format is 1 start bit, 1 stop bit, 8 bit ascii
adwiens 0:dbb85bfd22fd 107 void rtty_bit_tick()
adwiens 0:dbb85bfd22fd 108 {
adwiens 0:dbb85bfd22fd 109 if(txen) {
adwiens 0:dbb85bfd22fd 110 if(bitn < 0) {
adwiens 0:dbb85bfd22fd 111 ifreq = SPACE_FREQ; // start bit (space/0)
adwiens 0:dbb85bfd22fd 112 ++bitn;
adwiens 0:dbb85bfd22fd 113 } else if(bitn > 7) {
adwiens 0:dbb85bfd22fd 114 ifreq = MARK_FREQ; // stop bit (mark/1)
adwiens 0:dbb85bfd22fd 115 bitn = -1;
adwiens 0:dbb85bfd22fd 116 if(txchar != NULL && *txchar != NULL)
adwiens 0:dbb85bfd22fd 117 ++txchar; // go to next character
adwiens 0:dbb85bfd22fd 118 } else { // data bit
adwiens 0:dbb85bfd22fd 119 if(txchar != NULL && *txchar != NULL)
adwiens 0:dbb85bfd22fd 120 ifreq = ((*txchar & (1<<bitn)) == 0) ? SPACE_FREQ : MARK_FREQ;
adwiens 0:dbb85bfd22fd 121 else
adwiens 0:dbb85bfd22fd 122 ifreq = MARK_FREQ;
adwiens 0:dbb85bfd22fd 123 ++bitn;
adwiens 0:dbb85bfd22fd 124 }
adwiens 0:dbb85bfd22fd 125 }
adwiens 0:dbb85bfd22fd 126 }
adwiens 0:dbb85bfd22fd 127
adwiens 0:dbb85bfd22fd 128 void rtty_tx_thread(void const *argument)
adwiens 0:dbb85bfd22fd 129 {
adwiens 0:dbb85bfd22fd 130 while(1) {
adwiens 0:dbb85bfd22fd 131 txen = false;
adwiens 0:dbb85bfd22fd 132 ptt = 1; // turn off transmitter
adwiens 0:dbb85bfd22fd 133 tx_led = 0;
adwiens 0:dbb85bfd22fd 134 Thread::wait(RADIO_TX_WAIT); // wait for a certain amount of time between transmissions
adwiens 0:dbb85bfd22fd 135 txbuf.str(""); // empty tx buffer
adwiens 0:dbb85bfd22fd 136 txbuf.clear();
adwiens 0:dbb85bfd22fd 137 nmea_data_mutex.lock();
adwiens 0:dbb85bfd22fd 138 for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
adwiens 0:dbb85bfd22fd 139 txbuf << (iter->second); // fill the packet with the most recent nmea sentences
adwiens 0:dbb85bfd22fd 140 }
adwiens 0:dbb85bfd22fd 141 nmea_data_mutex.unlock();
adwiens 0:dbb85bfd22fd 142 txbufstr = txbuf.str();
adwiens 0:dbb85bfd22fd 143 txchar = txbufstr.c_str();
adwiens 0:dbb85bfd22fd 144 ptt = 0; // key up the radio
adwiens 0:dbb85bfd22fd 145 tx_led = 1;
adwiens 0:dbb85bfd22fd 146 txen = true;
adwiens 0:dbb85bfd22fd 147 Thread::wait(RADIO_KEYUP_DELAY); // wait for radio to key up
adwiens 0:dbb85bfd22fd 148 while(txchar != NULL && *txchar != NULL); // wait for rtty to finish
adwiens 0:dbb85bfd22fd 149 Thread::wait(RADIO_KEYUP_DELAY);
adwiens 0:dbb85bfd22fd 150 txen = false;
adwiens 0:dbb85bfd22fd 151 }
adwiens 0:dbb85bfd22fd 152 }
adwiens 0:dbb85bfd22fd 153
adwiens 0:dbb85bfd22fd 154 void print_nmea_data() // useful for debug
adwiens 0:dbb85bfd22fd 155 {
adwiens 0:dbb85bfd22fd 156 nmea_data_mutex.lock();
adwiens 0:dbb85bfd22fd 157 for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
adwiens 0:dbb85bfd22fd 158 cout << (iter->second);
adwiens 0:dbb85bfd22fd 159 }
adwiens 0:dbb85bfd22fd 160 nmea_data_mutex.unlock();
adwiens 0:dbb85bfd22fd 161 cout << endl;
adwiens 0:dbb85bfd22fd 162 }
adwiens 0:dbb85bfd22fd 163
adwiens 0:dbb85bfd22fd 164 int main()
adwiens 0:dbb85bfd22fd 165 {
adwiens 0:dbb85bfd22fd 166 Ticker sample_tick, bit_tick;
adwiens 0:dbb85bfd22fd 167 Thread gps_thread(gps_rx_thread); // gps receive thread
adwiens 0:dbb85bfd22fd 168 Thread rtty_thread(rtty_tx_thread); // rtty transmit thread
adwiens 0:dbb85bfd22fd 169 gps.baud(GPS_BAUD); // set gps bit rate
adwiens 0:dbb85bfd22fd 170 gps.attach(&gps_rx_isr); // set up gps receive interrupt service routine
adwiens 0:dbb85bfd22fd 171 sample_tick.attach_us(&rtty_sample_tick,1000000/AUDIO_FS); // begin generating audio
adwiens 0:dbb85bfd22fd 172 bit_tick.attach_us(&rtty_bit_tick,1000000/RTTY_BAUD); // begin sending characters
adwiens 0:dbb85bfd22fd 173 while(1) {
adwiens 0:dbb85bfd22fd 174 Thread::wait(2000);
adwiens 0:dbb85bfd22fd 175 print_nmea_data(); // debug
adwiens 0:dbb85bfd22fd 176 }
adwiens 0:dbb85bfd22fd 177 }