Generates RTTY from a GPS receiver
Dependencies: mbed-dsp mbed-rtos mbed
Diff: main.cpp
- Revision:
- 0:dbb85bfd22fd
- Child:
- 1:07d7070e9252
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Sat Jan 11 01:55:42 2014 +0000 @@ -0,0 +1,177 @@ +#include "mbed.h" +#include "rtos.h" // mbed real time os library +#include "dsp.h" // mbed digital signal processing library +#include <cstring> +#include <string> +#include <iostream> +#include <sstream> +#include <map> + +#define GPS_CB_SIZE (16) /* size of gps circular buffer in characters */ +#define GPS_BAUD (9600) /* gps serial port speed in bps */ +#define RADIO_TX_WAIT (2000) /* time between radio transmissions in ms */ +#define RADIO_KEYUP_DELAY (1000) /* time to wait for radio transmitter to turn on */ +#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 AUDIO_VOL (0.25) /* range 0-1 */ + +using namespace std; + +Serial pc(USBTX, USBRX); // pc serial port (via usb) + +// 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 +DigitalOut gps_led(LED1); // gps status led +Serial gps(p9, p10); // gps serial port (uart3) +stringstream rxbuf; // gps receive buffer +map<string,string> nmea_data; // most recent nmea sentences +Mutex nmea_data_mutex; // nmea data lock + +// 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 +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. +// 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() +{ + cb[cb_isr_i] = LPC_UART3->RBR; // avoid mutex lockup (https://mbed.org/forum/bugs-suggestions/topic/4217/) + if(++cb_isr_i >= GPS_CB_SIZE) cb_isr_i = 0; // loop circular buffer index +} + +// This function 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 +// this function updates a map containing K,V pairs. (The sentence +// type is the key and the value is the NMEA sentence.) +void gps_rx_thread(void const *argument) +{ + while(1) { + gps_led = 0; + Thread::wait((GPS_CB_SIZE)*1000*8/4/GPS_BAUD); // wait until cb 25% full + gps_led = 1; + while(cb_thr_i != cb_isr_i) { + char c = cb[cb_thr_i]; + rxbuf << c; // add to string buffer + if(c == '\n') { // clear string buffer + const char *c = rxbuf.str().c_str(); + const char *l = strchr(c, '$'), *r = strchr(c, ','); + if(l != NULL && r != NULL && r > l) { // check valid limits + char ctype[6]; + memcpy(ctype,l+1,5); + ctype[5] = 0; + string type = ctype; + nmea_data_mutex.lock(); + nmea_data[type] = rxbuf.str(); // update map + nmea_data_mutex.unlock(); + } + rxbuf.str(""); + rxbuf.clear(); + } + if(++cb_thr_i >= GPS_CB_SIZE) cb_thr_i = 0; // loop circular buffer index + } + } +} + +// This function writes individual audio samples to the dac. +void rtty_sample_tick() +{ + 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 + { + 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 +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; + ++bitn; + } + } +} + +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(); + 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; + } +} + +void print_nmea_data() // useful for debug +{ + 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; +} + +int main() +{ + Ticker sample_tick, bit_tick; + Thread gps_thread(gps_rx_thread); // gps receive thread + Thread rtty_thread(rtty_tx_thread); // rtty transmit 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 + } +}