Generates RTTY from a GPS receiver

Dependencies:   mbed-dsp mbed-rtos mbed

Revision:
1:07d7070e9252
Parent:
0:dbb85bfd22fd
Child:
2:1f19f8e52c75
--- a/main.cpp	Sat Jan 11 01:55:42 2014 +0000
+++ b/main.cpp	Sat Jan 11 05:46:49 2014 +0000
@@ -8,14 +8,17 @@
 #include <map>
 
 #define GPS_CB_SIZE (16) /* size of gps circular buffer in characters */
+#define RTTY_CB_SIZE (2048) /* characters in rtty buffer */
 #define GPS_BAUD (9600) /* gps serial port speed in bps */
-#define RADIO_TX_WAIT (2000) /* time between radio transmissions in ms */
+#define RADIO_TX_WAIT (20000) /* time between radio transmissions in ms */
 #define RADIO_KEYUP_DELAY (1000) /* time to wait for radio transmitter to turn on */
+#define PRINT_NMEA_WAIT (2000) /* time between console debug messages */
 #define AUDIO_FS (22050) /* audio sample rate in hz */
 #define RTTY_BAUD (45.45) /* rtty bit rate in bps */
-#define MARK_FREQ (2125) /* mark frequency (1) in hz */
-#define SPACE_FREQ (2295) /* space frequency (0) in hz */
+#define MARK_FREQ (2295) /* mark frequency (1) in hz */
+#define SPACE_FREQ (2125) /* space frequency (0) in hz */
 #define AUDIO_VOL (0.25) /* range 0-1 */
+#define RTTY_NUM_ZEROS (3) /* number of empty characters to append before each rtty message */
 
 using namespace std;
 
@@ -23,8 +26,8 @@
 
 // GPS variables:
 char cb[GPS_CB_SIZE]; // c-string circular buffer for gps rx isr
-int cb_isr_i = 0; // isr index
-int cb_thr_i = 0; // thread index
+int cb_isr_i = 0; // gps isr index
+int cb_thr_i = 0; // gps thread index
 DigitalOut gps_led(LED1); // gps status led
 Serial gps(p9, p10); // gps serial port (uart3)
 stringstream rxbuf; // gps receive buffer
@@ -34,16 +37,16 @@
 // RTTY variables:
 AnalogOut dac(p18); // mbed built-in digital to analog converter
 DigitalOut ptt(p17); // radio push to talk button
-DigitalOut tx_led(LED2); // tx status led
-stringstream txbuf; // rtty tx buffer
-string txbufstr;
-const char *txchar = NULL; // current character to transmit
+DigitalOut rtty_led(LED2); // tx status led
+char r_cb[RTTY_CB_SIZE]; // c-string circular buffer for rtty tx isr
+int r_cb_isr_i = 0; // rtty isr index
+int r_cb_thr_i = 0; // rtty thread index
 float angle = 0.0; // current sine angle
 int ifreq = MARK_FREQ; // instantaneous frequency
 int bitn = -1; // current bit number
 bool txen = false; // tx enable flag
 
-// This function is the interrupt service routine for the gps.
+// Interrupt service routine for the gps.
 // It is called when the serial port receives a character,
 // and it puts the character into a circular buffer for the gps thread.
 void gps_rx_isr()
@@ -52,7 +55,7 @@
     if(++cb_isr_i >= GPS_CB_SIZE) cb_isr_i = 0; // loop circular buffer index
 }
 
-// This function reads new characters from the gps circular
+// Reads new characters from the gps circular
 // buffer when the circular buffer is about 25 percent full
 // and adds the new characters to a string containing the current
 // nmea sentence. Each time a complete NMEA sentence is received
@@ -82,83 +85,84 @@
                 rxbuf.str("");
                 rxbuf.clear();
             }
-            if(++cb_thr_i >= GPS_CB_SIZE) cb_thr_i = 0; // loop circular buffer index
+            if(++cb_thr_i >= GPS_CB_SIZE) cb_thr_i = 0; // incr/loop circular buffer index
         }
     }
 }
 
-// This function writes individual audio samples to the dac.
+// Writes individual audio samples to the dac. It uses the
+// instantaneous frequency from the bit ticker.
 void rtty_sample_tick()
 {
-    if(txen)
-    {
+    if(txen) {
         angle += 2 * PI * ifreq / AUDIO_FS;
         if(angle > 2 * PI) angle -= 2*PI;
         dac = (arm_sin_f32(angle) + 1.0) / 2.0 * AUDIO_VOL; // write sample to dac
-    }
-    else
-    {
+    } else {
         dac = 0;
     }
 }
 
-// This function controls whether the current rtty bit is a mark or a space.
-// Format is 1 start bit, 1 stop bit, 8 bit ascii
+// Handles whether the current rtty bit is a mark or a space.
+// It reads one character at a time from the rtty circular buffer and sets
+// the instantaneous frequency of the sample ticker for each bit. (1==mark,
+// 0==space.)
 void rtty_bit_tick()
 {
-    if(txen) {
-        if(bitn < 0) {
-            ifreq = SPACE_FREQ; // start bit (space/0)
-            ++bitn;
-        } else if(bitn > 7) {
-            ifreq = MARK_FREQ; // stop bit (mark/1)
-            bitn = -1;
-            if(txchar != NULL && *txchar != NULL)
-                ++txchar; // go to next character
-        } else { // data bit
-            if(txchar != NULL && *txchar != NULL)
-                ifreq = ((*txchar & (1<<bitn)) == 0) ? SPACE_FREQ : MARK_FREQ;
-            else
-                ifreq = MARK_FREQ;
+    if(bitn < 0) {
+        txen = (r_cb_isr_i != r_cb_thr_i);
+        if(txen) {
+            ifreq = SPACE_FREQ; // start bit
+            if(++r_cb_isr_i >= RTTY_CB_SIZE) r_cb_isr_i = 0; // incr/loop circular buffer index
             ++bitn;
         }
+    } else if(bitn < 8 && txen) {
+        ifreq = ((r_cb[r_cb_isr_i] & (1<<(bitn++))) == 0) ? SPACE_FREQ : MARK_FREQ; // data bit
+    } else if(txen) {
+        ifreq = MARK_FREQ; // stop bit
+        bitn = -1;
     }
 }
 
+// Adds NMEA sentences periodically to a buffer for the other RTTY
+// functions to process.
 void rtty_tx_thread(void const *argument)
 {
     while(1) {
-        txen = false;
-        ptt = 1; // turn off transmitter
-        tx_led = 0;
         Thread::wait(RADIO_TX_WAIT); // wait for a certain amount of time between transmissions
-        txbuf.str(""); // empty tx buffer
-        txbuf.clear();
+        stringstream txbuf;
         nmea_data_mutex.lock();
         for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
             txbuf << (iter->second); // fill the packet with the most recent nmea sentences
         }
         nmea_data_mutex.unlock();
-        txbufstr = txbuf.str();
-        txchar = txbufstr.c_str();
-        ptt = 0; // key up the radio
-        tx_led = 1;
-        txen = true;
-        Thread::wait(RADIO_KEYUP_DELAY); // wait for radio to key up
-        while(txchar != NULL && *txchar != NULL); // wait for rtty to finish
-        Thread::wait(RADIO_KEYUP_DELAY);
-        txen = false;
+        for(int i = 0; i < RTTY_NUM_ZEROS; ++i) {
+            if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index
+            r_cb[r_cb_thr_i] = 0; // append a zero
+        }
+        for(const char* it = txbuf.str().c_str(); *it; ++it) { // add all characters to buffer
+            if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index
+            r_cb[r_cb_thr_i] = *it;
+        }
+        for(int i = 0; i < RTTY_NUM_ZEROS; ++i) {
+            if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index
+            r_cb[r_cb_thr_i] = 0; // append a zero
+        }
     }
 }
 
-void print_nmea_data()   // useful for debug
+// Writes debug messages periodically to the console. Useful for debug.
+void print_nmea_thread(void const *argument)
 {
-    nmea_data_mutex.lock();
-    for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
-        cout << (iter->second);
+    while(1) {
+        nmea_data_mutex.lock();
+        for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
+            cout << (iter->second);
+        }
+        nmea_data_mutex.unlock();
+        cout << endl;
+        Thread::wait(PRINT_NMEA_WAIT);
     }
-    nmea_data_mutex.unlock();
-    cout << endl;
 }
 
 int main()
@@ -166,12 +170,10 @@
     Ticker sample_tick, bit_tick;
     Thread gps_thread(gps_rx_thread); // gps receive thread
     Thread rtty_thread(rtty_tx_thread); // rtty transmit thread
+    Thread print_thread(print_nmea_thread); // debug print thread
     gps.baud(GPS_BAUD); // set gps bit rate
     gps.attach(&gps_rx_isr); // set up gps receive interrupt service routine
     sample_tick.attach_us(&rtty_sample_tick,1000000/AUDIO_FS); // begin generating audio
     bit_tick.attach_us(&rtty_bit_tick,1000000/RTTY_BAUD); // begin sending characters
-    while(1) {
-        Thread::wait(2000);
-        print_nmea_data(); // debug
-    }
+    while(1); // idle forever
 }