Generates RTTY from a GPS receiver
Dependencies: mbed-dsp mbed-rtos mbed
main.cpp@2:1f19f8e52c75, 2014-01-11 (annotated)
- Committer:
- adwiens
- Date:
- Sat Jan 11 05:55:54 2014 +0000
- Revision:
- 2:1f19f8e52c75
- Parent:
- 1:07d7070e9252
- Child:
- 3:33d80761aa06
some minor tweaks
Who changed what in which revision?
User | Revision | Line number | New 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 | 1:07d7070e9252 | 11 | #define RTTY_CB_SIZE (2048) /* characters in rtty buffer */ |
adwiens | 0:dbb85bfd22fd | 12 | #define GPS_BAUD (9600) /* gps serial port speed in bps */ |
adwiens | 2:1f19f8e52c75 | 13 | #define RADIO_TX_WAIT (5000) /* time between radio transmissions in ms */ |
adwiens | 0:dbb85bfd22fd | 14 | #define RADIO_KEYUP_DELAY (1000) /* time to wait for radio transmitter to turn on */ |
adwiens | 1:07d7070e9252 | 15 | #define PRINT_NMEA_WAIT (2000) /* time between console debug messages */ |
adwiens | 0:dbb85bfd22fd | 16 | #define AUDIO_FS (22050) /* audio sample rate in hz */ |
adwiens | 0:dbb85bfd22fd | 17 | #define RTTY_BAUD (45.45) /* rtty bit rate in bps */ |
adwiens | 1:07d7070e9252 | 18 | #define MARK_FREQ (2295) /* mark frequency (1) in hz */ |
adwiens | 1:07d7070e9252 | 19 | #define SPACE_FREQ (2125) /* space frequency (0) in hz */ |
adwiens | 0:dbb85bfd22fd | 20 | #define AUDIO_VOL (0.25) /* range 0-1 */ |
adwiens | 1:07d7070e9252 | 21 | #define RTTY_NUM_ZEROS (3) /* number of empty characters to append before each rtty message */ |
adwiens | 0:dbb85bfd22fd | 22 | |
adwiens | 0:dbb85bfd22fd | 23 | using namespace std; |
adwiens | 0:dbb85bfd22fd | 24 | |
adwiens | 0:dbb85bfd22fd | 25 | Serial pc(USBTX, USBRX); // pc serial port (via usb) |
adwiens | 0:dbb85bfd22fd | 26 | |
adwiens | 0:dbb85bfd22fd | 27 | // GPS variables: |
adwiens | 0:dbb85bfd22fd | 28 | char cb[GPS_CB_SIZE]; // c-string circular buffer for gps rx isr |
adwiens | 1:07d7070e9252 | 29 | int cb_isr_i = 0; // gps isr index |
adwiens | 1:07d7070e9252 | 30 | int cb_thr_i = 0; // gps thread index |
adwiens | 0:dbb85bfd22fd | 31 | DigitalOut gps_led(LED1); // gps status led |
adwiens | 0:dbb85bfd22fd | 32 | Serial gps(p9, p10); // gps serial port (uart3) |
adwiens | 0:dbb85bfd22fd | 33 | stringstream rxbuf; // gps receive buffer |
adwiens | 0:dbb85bfd22fd | 34 | map<string,string> nmea_data; // most recent nmea sentences |
adwiens | 0:dbb85bfd22fd | 35 | Mutex nmea_data_mutex; // nmea data lock |
adwiens | 0:dbb85bfd22fd | 36 | |
adwiens | 0:dbb85bfd22fd | 37 | // RTTY variables: |
adwiens | 0:dbb85bfd22fd | 38 | AnalogOut dac(p18); // mbed built-in digital to analog converter |
adwiens | 0:dbb85bfd22fd | 39 | DigitalOut ptt(p17); // radio push to talk button |
adwiens | 1:07d7070e9252 | 40 | DigitalOut rtty_led(LED2); // tx status led |
adwiens | 1:07d7070e9252 | 41 | char r_cb[RTTY_CB_SIZE]; // c-string circular buffer for rtty tx isr |
adwiens | 1:07d7070e9252 | 42 | int r_cb_isr_i = 0; // rtty isr index |
adwiens | 1:07d7070e9252 | 43 | int r_cb_thr_i = 0; // rtty thread index |
adwiens | 0:dbb85bfd22fd | 44 | float angle = 0.0; // current sine angle |
adwiens | 0:dbb85bfd22fd | 45 | int ifreq = MARK_FREQ; // instantaneous frequency |
adwiens | 0:dbb85bfd22fd | 46 | int bitn = -1; // current bit number |
adwiens | 0:dbb85bfd22fd | 47 | bool txen = false; // tx enable flag |
adwiens | 0:dbb85bfd22fd | 48 | |
adwiens | 1:07d7070e9252 | 49 | // Interrupt service routine for the gps. |
adwiens | 0:dbb85bfd22fd | 50 | // It is called when the serial port receives a character, |
adwiens | 0:dbb85bfd22fd | 51 | // and it puts the character into a circular buffer for the gps thread. |
adwiens | 0:dbb85bfd22fd | 52 | void gps_rx_isr() |
adwiens | 0:dbb85bfd22fd | 53 | { |
adwiens | 0:dbb85bfd22fd | 54 | cb[cb_isr_i] = LPC_UART3->RBR; // avoid mutex lockup (https://mbed.org/forum/bugs-suggestions/topic/4217/) |
adwiens | 0:dbb85bfd22fd | 55 | if(++cb_isr_i >= GPS_CB_SIZE) cb_isr_i = 0; // loop circular buffer index |
adwiens | 0:dbb85bfd22fd | 56 | } |
adwiens | 0:dbb85bfd22fd | 57 | |
adwiens | 1:07d7070e9252 | 58 | // Reads new characters from the gps circular |
adwiens | 0:dbb85bfd22fd | 59 | // buffer when the circular buffer is about 25 percent full |
adwiens | 0:dbb85bfd22fd | 60 | // and adds the new characters to a string containing the current |
adwiens | 0:dbb85bfd22fd | 61 | // nmea sentence. Each time a complete NMEA sentence is received |
adwiens | 0:dbb85bfd22fd | 62 | // this function updates a map containing K,V pairs. (The sentence |
adwiens | 0:dbb85bfd22fd | 63 | // type is the key and the value is the NMEA sentence.) |
adwiens | 0:dbb85bfd22fd | 64 | void gps_rx_thread(void const *argument) |
adwiens | 0:dbb85bfd22fd | 65 | { |
adwiens | 0:dbb85bfd22fd | 66 | while(1) { |
adwiens | 0:dbb85bfd22fd | 67 | gps_led = 0; |
adwiens | 0:dbb85bfd22fd | 68 | Thread::wait((GPS_CB_SIZE)*1000*8/4/GPS_BAUD); // wait until cb 25% full |
adwiens | 0:dbb85bfd22fd | 69 | gps_led = 1; |
adwiens | 0:dbb85bfd22fd | 70 | while(cb_thr_i != cb_isr_i) { |
adwiens | 0:dbb85bfd22fd | 71 | char c = cb[cb_thr_i]; |
adwiens | 0:dbb85bfd22fd | 72 | rxbuf << c; // add to string buffer |
adwiens | 0:dbb85bfd22fd | 73 | if(c == '\n') { // clear string buffer |
adwiens | 0:dbb85bfd22fd | 74 | const char *c = rxbuf.str().c_str(); |
adwiens | 0:dbb85bfd22fd | 75 | const char *l = strchr(c, '$'), *r = strchr(c, ','); |
adwiens | 0:dbb85bfd22fd | 76 | if(l != NULL && r != NULL && r > l) { // check valid limits |
adwiens | 0:dbb85bfd22fd | 77 | char ctype[6]; |
adwiens | 0:dbb85bfd22fd | 78 | memcpy(ctype,l+1,5); |
adwiens | 0:dbb85bfd22fd | 79 | ctype[5] = 0; |
adwiens | 0:dbb85bfd22fd | 80 | string type = ctype; |
adwiens | 0:dbb85bfd22fd | 81 | nmea_data_mutex.lock(); |
adwiens | 0:dbb85bfd22fd | 82 | nmea_data[type] = rxbuf.str(); // update map |
adwiens | 0:dbb85bfd22fd | 83 | nmea_data_mutex.unlock(); |
adwiens | 0:dbb85bfd22fd | 84 | } |
adwiens | 0:dbb85bfd22fd | 85 | rxbuf.str(""); |
adwiens | 0:dbb85bfd22fd | 86 | rxbuf.clear(); |
adwiens | 0:dbb85bfd22fd | 87 | } |
adwiens | 1:07d7070e9252 | 88 | if(++cb_thr_i >= GPS_CB_SIZE) cb_thr_i = 0; // incr/loop circular buffer index |
adwiens | 0:dbb85bfd22fd | 89 | } |
adwiens | 0:dbb85bfd22fd | 90 | } |
adwiens | 0:dbb85bfd22fd | 91 | } |
adwiens | 0:dbb85bfd22fd | 92 | |
adwiens | 1:07d7070e9252 | 93 | // Writes individual audio samples to the dac. It uses the |
adwiens | 1:07d7070e9252 | 94 | // instantaneous frequency from the bit ticker. |
adwiens | 0:dbb85bfd22fd | 95 | void rtty_sample_tick() |
adwiens | 0:dbb85bfd22fd | 96 | { |
adwiens | 1:07d7070e9252 | 97 | if(txen) { |
adwiens | 0:dbb85bfd22fd | 98 | angle += 2 * PI * ifreq / AUDIO_FS; |
adwiens | 0:dbb85bfd22fd | 99 | if(angle > 2 * PI) angle -= 2*PI; |
adwiens | 0:dbb85bfd22fd | 100 | dac = (arm_sin_f32(angle) + 1.0) / 2.0 * AUDIO_VOL; // write sample to dac |
adwiens | 2:1f19f8e52c75 | 101 | ptt = 0; |
adwiens | 2:1f19f8e52c75 | 102 | rtty_led = 1; |
adwiens | 1:07d7070e9252 | 103 | } else { |
adwiens | 0:dbb85bfd22fd | 104 | dac = 0; |
adwiens | 2:1f19f8e52c75 | 105 | ptt = 1; |
adwiens | 2:1f19f8e52c75 | 106 | rtty_led = 0; |
adwiens | 0:dbb85bfd22fd | 107 | } |
adwiens | 0:dbb85bfd22fd | 108 | } |
adwiens | 0:dbb85bfd22fd | 109 | |
adwiens | 1:07d7070e9252 | 110 | // Handles whether the current rtty bit is a mark or a space. |
adwiens | 1:07d7070e9252 | 111 | // It reads one character at a time from the rtty circular buffer and sets |
adwiens | 1:07d7070e9252 | 112 | // the instantaneous frequency of the sample ticker for each bit. (1==mark, |
adwiens | 1:07d7070e9252 | 113 | // 0==space.) |
adwiens | 0:dbb85bfd22fd | 114 | void rtty_bit_tick() |
adwiens | 0:dbb85bfd22fd | 115 | { |
adwiens | 1:07d7070e9252 | 116 | if(bitn < 0) { |
adwiens | 1:07d7070e9252 | 117 | txen = (r_cb_isr_i != r_cb_thr_i); |
adwiens | 1:07d7070e9252 | 118 | if(txen) { |
adwiens | 1:07d7070e9252 | 119 | ifreq = SPACE_FREQ; // start bit |
adwiens | 1:07d7070e9252 | 120 | if(++r_cb_isr_i >= RTTY_CB_SIZE) r_cb_isr_i = 0; // incr/loop circular buffer index |
adwiens | 0:dbb85bfd22fd | 121 | ++bitn; |
adwiens | 0:dbb85bfd22fd | 122 | } |
adwiens | 1:07d7070e9252 | 123 | } else if(bitn < 8 && txen) { |
adwiens | 1:07d7070e9252 | 124 | ifreq = ((r_cb[r_cb_isr_i] & (1<<(bitn++))) == 0) ? SPACE_FREQ : MARK_FREQ; // data bit |
adwiens | 1:07d7070e9252 | 125 | } else if(txen) { |
adwiens | 1:07d7070e9252 | 126 | ifreq = MARK_FREQ; // stop bit |
adwiens | 1:07d7070e9252 | 127 | bitn = -1; |
adwiens | 0:dbb85bfd22fd | 128 | } |
adwiens | 0:dbb85bfd22fd | 129 | } |
adwiens | 0:dbb85bfd22fd | 130 | |
adwiens | 2:1f19f8e52c75 | 131 | // Adds empty characters to RTTY buffer. |
adwiens | 2:1f19f8e52c75 | 132 | void rtty_add_padding() |
adwiens | 2:1f19f8e52c75 | 133 | { |
adwiens | 2:1f19f8e52c75 | 134 | for(int i = 0; i < RTTY_NUM_ZEROS; ++i) { |
adwiens | 2:1f19f8e52c75 | 135 | if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index |
adwiens | 2:1f19f8e52c75 | 136 | r_cb[r_cb_thr_i] = 0; // append a zero |
adwiens | 2:1f19f8e52c75 | 137 | } |
adwiens | 2:1f19f8e52c75 | 138 | } |
adwiens | 2:1f19f8e52c75 | 139 | |
adwiens | 1:07d7070e9252 | 140 | // Adds NMEA sentences periodically to a buffer for the other RTTY |
adwiens | 1:07d7070e9252 | 141 | // functions to process. |
adwiens | 0:dbb85bfd22fd | 142 | void rtty_tx_thread(void const *argument) |
adwiens | 0:dbb85bfd22fd | 143 | { |
adwiens | 0:dbb85bfd22fd | 144 | while(1) { |
adwiens | 0:dbb85bfd22fd | 145 | Thread::wait(RADIO_TX_WAIT); // wait for a certain amount of time between transmissions |
adwiens | 1:07d7070e9252 | 146 | stringstream txbuf; |
adwiens | 0:dbb85bfd22fd | 147 | nmea_data_mutex.lock(); |
adwiens | 0:dbb85bfd22fd | 148 | for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) { |
adwiens | 0:dbb85bfd22fd | 149 | txbuf << (iter->second); // fill the packet with the most recent nmea sentences |
adwiens | 0:dbb85bfd22fd | 150 | } |
adwiens | 0:dbb85bfd22fd | 151 | nmea_data_mutex.unlock(); |
adwiens | 2:1f19f8e52c75 | 152 | rtty_add_padding(); // pad message with empty characters |
adwiens | 1:07d7070e9252 | 153 | for(const char* it = txbuf.str().c_str(); *it; ++it) { // add all characters to buffer |
adwiens | 1:07d7070e9252 | 154 | if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index |
adwiens | 1:07d7070e9252 | 155 | r_cb[r_cb_thr_i] = *it; |
adwiens | 1:07d7070e9252 | 156 | } |
adwiens | 2:1f19f8e52c75 | 157 | rtty_add_padding(); |
adwiens | 2:1f19f8e52c75 | 158 | while(!txen) Thread::wait(100); // wait for transmission to start |
adwiens | 2:1f19f8e52c75 | 159 | while( txen) Thread::wait(100); // wait for transmission to end |
adwiens | 0:dbb85bfd22fd | 160 | } |
adwiens | 0:dbb85bfd22fd | 161 | } |
adwiens | 0:dbb85bfd22fd | 162 | |
adwiens | 1:07d7070e9252 | 163 | // Writes debug messages periodically to the console. Useful for debug. |
adwiens | 1:07d7070e9252 | 164 | void print_nmea_thread(void const *argument) |
adwiens | 0:dbb85bfd22fd | 165 | { |
adwiens | 1:07d7070e9252 | 166 | while(1) { |
adwiens | 1:07d7070e9252 | 167 | nmea_data_mutex.lock(); |
adwiens | 1:07d7070e9252 | 168 | for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) { |
adwiens | 1:07d7070e9252 | 169 | cout << (iter->second); |
adwiens | 1:07d7070e9252 | 170 | } |
adwiens | 1:07d7070e9252 | 171 | nmea_data_mutex.unlock(); |
adwiens | 1:07d7070e9252 | 172 | cout << endl; |
adwiens | 1:07d7070e9252 | 173 | Thread::wait(PRINT_NMEA_WAIT); |
adwiens | 0:dbb85bfd22fd | 174 | } |
adwiens | 0:dbb85bfd22fd | 175 | } |
adwiens | 0:dbb85bfd22fd | 176 | |
adwiens | 0:dbb85bfd22fd | 177 | int main() |
adwiens | 0:dbb85bfd22fd | 178 | { |
adwiens | 0:dbb85bfd22fd | 179 | Ticker sample_tick, bit_tick; |
adwiens | 0:dbb85bfd22fd | 180 | Thread gps_thread(gps_rx_thread); // gps receive thread |
adwiens | 0:dbb85bfd22fd | 181 | Thread rtty_thread(rtty_tx_thread); // rtty transmit thread |
adwiens | 1:07d7070e9252 | 182 | Thread print_thread(print_nmea_thread); // debug print thread |
adwiens | 0:dbb85bfd22fd | 183 | gps.baud(GPS_BAUD); // set gps bit rate |
adwiens | 0:dbb85bfd22fd | 184 | gps.attach(&gps_rx_isr); // set up gps receive interrupt service routine |
adwiens | 0:dbb85bfd22fd | 185 | sample_tick.attach_us(&rtty_sample_tick,1000000/AUDIO_FS); // begin generating audio |
adwiens | 0:dbb85bfd22fd | 186 | bit_tick.attach_us(&rtty_bit_tick,1000000/RTTY_BAUD); // begin sending characters |
adwiens | 1:07d7070e9252 | 187 | while(1); // idle forever |
adwiens | 0:dbb85bfd22fd | 188 | } |