Generates RTTY from a GPS receiver

Dependencies:   mbed-dsp mbed-rtos mbed

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?

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 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 }