Generates RTTY from a GPS receiver

Dependencies:   mbed-dsp mbed-rtos mbed

Committer:
adwiens
Date:
Sat Jan 11 05:46:49 2014 +0000
Revision:
1:07d7070e9252
Parent:
0:dbb85bfd22fd
Child:
2:1f19f8e52c75
working!

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 1:07d7070e9252 13 #define RADIO_TX_WAIT (20000) /* 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 1:07d7070e9252 101 } else {
adwiens 0:dbb85bfd22fd 102 dac = 0;
adwiens 0:dbb85bfd22fd 103 }
adwiens 0:dbb85bfd22fd 104 }
adwiens 0:dbb85bfd22fd 105
adwiens 1:07d7070e9252 106 // Handles whether the current rtty bit is a mark or a space.
adwiens 1:07d7070e9252 107 // It reads one character at a time from the rtty circular buffer and sets
adwiens 1:07d7070e9252 108 // the instantaneous frequency of the sample ticker for each bit. (1==mark,
adwiens 1:07d7070e9252 109 // 0==space.)
adwiens 0:dbb85bfd22fd 110 void rtty_bit_tick()
adwiens 0:dbb85bfd22fd 111 {
adwiens 1:07d7070e9252 112 if(bitn < 0) {
adwiens 1:07d7070e9252 113 txen = (r_cb_isr_i != r_cb_thr_i);
adwiens 1:07d7070e9252 114 if(txen) {
adwiens 1:07d7070e9252 115 ifreq = SPACE_FREQ; // start bit
adwiens 1:07d7070e9252 116 if(++r_cb_isr_i >= RTTY_CB_SIZE) r_cb_isr_i = 0; // incr/loop circular buffer index
adwiens 0:dbb85bfd22fd 117 ++bitn;
adwiens 0:dbb85bfd22fd 118 }
adwiens 1:07d7070e9252 119 } else if(bitn < 8 && txen) {
adwiens 1:07d7070e9252 120 ifreq = ((r_cb[r_cb_isr_i] & (1<<(bitn++))) == 0) ? SPACE_FREQ : MARK_FREQ; // data bit
adwiens 1:07d7070e9252 121 } else if(txen) {
adwiens 1:07d7070e9252 122 ifreq = MARK_FREQ; // stop bit
adwiens 1:07d7070e9252 123 bitn = -1;
adwiens 0:dbb85bfd22fd 124 }
adwiens 0:dbb85bfd22fd 125 }
adwiens 0:dbb85bfd22fd 126
adwiens 1:07d7070e9252 127 // Adds NMEA sentences periodically to a buffer for the other RTTY
adwiens 1:07d7070e9252 128 // functions to process.
adwiens 0:dbb85bfd22fd 129 void rtty_tx_thread(void const *argument)
adwiens 0:dbb85bfd22fd 130 {
adwiens 0:dbb85bfd22fd 131 while(1) {
adwiens 0:dbb85bfd22fd 132 Thread::wait(RADIO_TX_WAIT); // wait for a certain amount of time between transmissions
adwiens 1:07d7070e9252 133 stringstream txbuf;
adwiens 0:dbb85bfd22fd 134 nmea_data_mutex.lock();
adwiens 0:dbb85bfd22fd 135 for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
adwiens 0:dbb85bfd22fd 136 txbuf << (iter->second); // fill the packet with the most recent nmea sentences
adwiens 0:dbb85bfd22fd 137 }
adwiens 0:dbb85bfd22fd 138 nmea_data_mutex.unlock();
adwiens 1:07d7070e9252 139 for(int i = 0; i < RTTY_NUM_ZEROS; ++i) {
adwiens 1:07d7070e9252 140 if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index
adwiens 1:07d7070e9252 141 r_cb[r_cb_thr_i] = 0; // append a zero
adwiens 1:07d7070e9252 142 }
adwiens 1:07d7070e9252 143 for(const char* it = txbuf.str().c_str(); *it; ++it) { // add all characters to buffer
adwiens 1:07d7070e9252 144 if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index
adwiens 1:07d7070e9252 145 r_cb[r_cb_thr_i] = *it;
adwiens 1:07d7070e9252 146 }
adwiens 1:07d7070e9252 147 for(int i = 0; i < RTTY_NUM_ZEROS; ++i) {
adwiens 1:07d7070e9252 148 if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index
adwiens 1:07d7070e9252 149 r_cb[r_cb_thr_i] = 0; // append a zero
adwiens 1:07d7070e9252 150 }
adwiens 0:dbb85bfd22fd 151 }
adwiens 0:dbb85bfd22fd 152 }
adwiens 0:dbb85bfd22fd 153
adwiens 1:07d7070e9252 154 // Writes debug messages periodically to the console. Useful for debug.
adwiens 1:07d7070e9252 155 void print_nmea_thread(void const *argument)
adwiens 0:dbb85bfd22fd 156 {
adwiens 1:07d7070e9252 157 while(1) {
adwiens 1:07d7070e9252 158 nmea_data_mutex.lock();
adwiens 1:07d7070e9252 159 for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) {
adwiens 1:07d7070e9252 160 cout << (iter->second);
adwiens 1:07d7070e9252 161 }
adwiens 1:07d7070e9252 162 nmea_data_mutex.unlock();
adwiens 1:07d7070e9252 163 cout << endl;
adwiens 1:07d7070e9252 164 Thread::wait(PRINT_NMEA_WAIT);
adwiens 0:dbb85bfd22fd 165 }
adwiens 0:dbb85bfd22fd 166 }
adwiens 0:dbb85bfd22fd 167
adwiens 0:dbb85bfd22fd 168 int main()
adwiens 0:dbb85bfd22fd 169 {
adwiens 0:dbb85bfd22fd 170 Ticker sample_tick, bit_tick;
adwiens 0:dbb85bfd22fd 171 Thread gps_thread(gps_rx_thread); // gps receive thread
adwiens 0:dbb85bfd22fd 172 Thread rtty_thread(rtty_tx_thread); // rtty transmit thread
adwiens 1:07d7070e9252 173 Thread print_thread(print_nmea_thread); // debug print thread
adwiens 0:dbb85bfd22fd 174 gps.baud(GPS_BAUD); // set gps bit rate
adwiens 0:dbb85bfd22fd 175 gps.attach(&gps_rx_isr); // set up gps receive interrupt service routine
adwiens 0:dbb85bfd22fd 176 sample_tick.attach_us(&rtty_sample_tick,1000000/AUDIO_FS); // begin generating audio
adwiens 0:dbb85bfd22fd 177 bit_tick.attach_us(&rtty_bit_tick,1000000/RTTY_BAUD); // begin sending characters
adwiens 1:07d7070e9252 178 while(1); // idle forever
adwiens 0:dbb85bfd22fd 179 }