Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: mbed-dsp mbed-rtos mbed
main.cpp
00001 // Author: Andrew Wiens (adwiens.com) 00002 // Date: 11 Jan 2014 00003 // 00004 // ========================================================================== 00005 // 00006 // GPS2RTTY 00007 // 00008 // Using the mbed RTOS and DSP libraries, GPS2RTTY generates RTTY messages 00009 // containing raw NMEA sentences from a GPS receiver. 00010 // 00011 // No extra hardware needed! Audio is generated with the mbed's built-in DAC. 00012 // 00013 // GPS ---> mbed ---> Radio 00014 // 00015 // ========================================================================== 00016 00017 #include "mbed.h" 00018 #include "rtos.h" // mbed real time os library 00019 #include "dsp.h" // mbed digital signal processing library 00020 #include <cstring> 00021 #include <string> 00022 #include <iostream> 00023 #include <sstream> 00024 #include <map> 00025 00026 #define GPS_CB_SIZE (16) /* size of gps circular buffer in characters */ 00027 #define RTTY_CB_SIZE (2048) /* characters in rtty buffer */ 00028 #define GPS_BAUD (9600) /* gps serial port speed in bps */ 00029 #define RADIO_TX_WAIT (1000) /* time between radio transmissions in ms */ 00030 #define PRINT_NMEA_WAIT (2000) /* time between console debug messages in ms */ 00031 #define CALLSIGN_WAIT (600) /* time between sending amateur radio callsign in s */ 00032 #define AUDIO_FS (8000) /* audio sample rate in hz */ 00033 #define RTTY_BAUD (110) /* rtty bit rate in bps */ 00034 #define MARK_FREQ (2850) /* mark frequency (1) in hz */ 00035 #define SPACE_FREQ (2000) /* space frequency (0) in hz */ 00036 #define AUDIO_VOL (1) /* range 0-1 */ 00037 #define RTTY_NUM_ZEROS_BEFORE (25) /* number of empty characters to append before each rtty message. helps receivers sync. */ 00038 #define RTTY_NUM_ZEROS_AFTER (8) /* number of empty characters to append after each rtty message. */ 00039 #define CALLSIGN "KC0WYS" /* my amateur radio callsign */ 00040 #define CALLSIGN_TAG "HAMID" /* 5-character tag used in the fake nmea sentence */ 00041 00042 using namespace std; 00043 00044 // mbed ports: 00045 Serial pc(USBTX, USBRX); // pc serial port (via usb) 00046 Serial gps(p9, p10); // gps serial port (uart3) 00047 AnalogOut dac(p18); // mbed built-in digital to analog converter 00048 DigitalOut ptt(p17); // radio push to talk button 00049 00050 // Status leds: 00051 DigitalOut gps_led(LED1); // gps status led 00052 DigitalOut rtty_led(LED2); // tx status led 00053 00054 // GPS variables: 00055 char cb[GPS_CB_SIZE]; // c-string circular buffer for gps rx isr 00056 int cb_isr_i = 0; // gps isr index 00057 int cb_thr_i = 0; // gps thread index 00058 stringstream rxbuf; // gps receive buffer 00059 map<string,string> nmea_data; // most recent nmea sentences 00060 Mutex nmea_data_mutex; // nmea data lock 00061 00062 // RTTY variables: 00063 char r_cb[RTTY_CB_SIZE]; // c-string circular buffer for rtty tx isr 00064 int r_cb_isr_i = 0; // rtty isr index 00065 int r_cb_thr_i = 0; // rtty thread index 00066 float angle = 0.0; // current sine angle 00067 int ifreq = MARK_FREQ; // instantaneous frequency 00068 int bitn = -1; // current bit number 00069 bool txen = false; // tx enable flag 00070 00071 // Interrupt service routine for the GPS. 00072 // It is called when the serial port receives a character, 00073 // and it puts the character into a circular buffer for the gps thread. 00074 void gps_rx_isr() 00075 { 00076 cb[cb_isr_i] = LPC_UART3->RBR; // avoid mutex lockup (https://mbed.org/forum/bugs-suggestions/topic/4217/) 00077 if(++cb_isr_i >= GPS_CB_SIZE) cb_isr_i = 0; // loop circular buffer index 00078 } 00079 00080 // Reads new characters from the GPS circular 00081 // buffer when the circular buffer is about 25 percent full 00082 // and adds the new characters to a string containing the current 00083 // nmea sentence. Each time a complete NMEA sentence is received 00084 // this function updates a map containing K,V pairs. (The sentence 00085 // type is the key and the value is the NMEA sentence.) 00086 void gps_rx_thread(void const *argument) 00087 { 00088 while(1) { 00089 gps_led = 0; 00090 Thread::wait((GPS_CB_SIZE)*1000*8/4/GPS_BAUD); // wait until cb 25% full 00091 gps_led = 1; 00092 while(cb_thr_i != cb_isr_i) { 00093 char c = cb[cb_thr_i]; 00094 rxbuf << c; // add to string buffer 00095 if(c == '\n') { // clear string buffer 00096 const char *c = rxbuf.str().c_str(); 00097 const char *l = strchr(c, '$'), *r = strchr(c, ','); 00098 if(l != NULL && r != NULL && r > l) { // check valid limits 00099 char ctype[6]; 00100 memcpy(ctype,l+1,5); 00101 ctype[5] = 0; 00102 string type = ctype; 00103 nmea_data_mutex.lock(); 00104 nmea_data[type] = rxbuf.str(); // update map 00105 nmea_data_mutex.unlock(); 00106 } 00107 rxbuf.str(""); 00108 rxbuf.clear(); 00109 } 00110 if(++cb_thr_i >= GPS_CB_SIZE) cb_thr_i = 0; // incr/loop circular buffer index 00111 } 00112 } 00113 } 00114 00115 // Writes individual audio samples to the dac. It uses the 00116 // instantaneous frequency from the bit ticker. 00117 void rtty_sample_tick() 00118 { 00119 if(txen) { 00120 angle += 2 * PI * ifreq / AUDIO_FS; 00121 if(angle > 2 * PI) angle -= 2*PI; 00122 dac = (arm_sin_f32(angle) + 1.0) / 2.0 * AUDIO_VOL; // write sample to dac 00123 ptt = 0; 00124 rtty_led = !rtty_led; 00125 } else { 00126 dac = 0; 00127 ptt = 1; 00128 rtty_led = 0; 00129 } 00130 } 00131 00132 // Handles whether the current rtty bit is a mark or a space. 00133 // It reads one character at a time from the rtty circular buffer and sets 00134 // the instantaneous frequency of the sample ticker for each bit. (1==mark, 00135 // 0==space.) 00136 void rtty_bit_tick() 00137 { 00138 if(bitn < 0) { 00139 txen = (r_cb_isr_i != r_cb_thr_i); 00140 if(txen) { 00141 ifreq = SPACE_FREQ; // start bit 00142 if(++r_cb_isr_i >= RTTY_CB_SIZE) r_cb_isr_i = 0; // incr/loop circular buffer index 00143 ++bitn; 00144 } 00145 } else if(bitn < 8 && txen) { 00146 ifreq = ((r_cb[r_cb_isr_i] & (1<<(bitn++))) == 0) ? SPACE_FREQ : MARK_FREQ; // data bit 00147 } else if(txen) { 00148 ifreq = MARK_FREQ; // stop bit 00149 bitn = -1; 00150 } 00151 } 00152 00153 // Adds NMEA sentences periodically to a buffer for the other RTTY functions to process. 00154 void rtty_tx_thread(void const *argument) 00155 { 00156 while(1) { 00157 Thread::wait(RADIO_TX_WAIT); // wait for a certain amount of time between transmissions 00158 stringstream txbuf; 00159 nmea_data_mutex.lock(); 00160 for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) { 00161 txbuf << (iter->second); // fill the packet with the most recent nmea sentences 00162 } 00163 nmea_data.clear(); // empty the map 00164 nmea_data_mutex.unlock(); 00165 for(int i = 0; i < RTTY_NUM_ZEROS_BEFORE; ++i) { // add empty characters 00166 if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index 00167 r_cb[r_cb_thr_i] = 0; // append a zero character 00168 } 00169 for(const char* it = txbuf.str().c_str(); *it; ++it) { // add all characters to buffer 00170 if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index 00171 r_cb[r_cb_thr_i] = *it; 00172 } 00173 for(int i = 0; i < RTTY_NUM_ZEROS_AFTER; ++i) { // add empty characters 00174 if(++r_cb_thr_i >= RTTY_CB_SIZE) r_cb_thr_i = 0; // incr/loop circular buffer index 00175 r_cb[r_cb_thr_i] = 0; // append a zero character 00176 } 00177 while(!txen) Thread::wait(100); // wait for transmission to start 00178 while( txen) Thread::wait(100); // wait for transmission to end 00179 } 00180 } 00181 00182 // Periodically inserts HAM radio callsign into NMEA sentence map. 00183 // Required by FCC to send callsign at least every 30 minutes. 00184 void insert_callsign_thread(void const *argument) 00185 { 00186 while(1) { 00187 nmea_data_mutex.lock(); 00188 nmea_data[CALLSIGN_TAG] = "$" CALLSIGN_TAG "," CALLSIGN "," CALLSIGN "," CALLSIGN ",*00\r\n"; // dummy nmea sentence 00189 nmea_data_mutex.unlock(); 00190 for(int i = 0; i < CALLSIGN_WAIT; ++i) Thread::wait(1000); // wait for CALLSIGN_WAIT seconds 00191 } 00192 } 00193 00194 // Writes NMEA sentences to the console. Useful for debug. 00195 void print_nmea_thread(void const *argument) 00196 { 00197 while(1) { 00198 nmea_data_mutex.lock(); 00199 for (map<string,string>::iterator iter = nmea_data.begin(); iter != nmea_data.end(); ++iter) { 00200 cout << (iter->second); 00201 } 00202 nmea_data_mutex.unlock(); 00203 cout << endl; 00204 Thread::wait(PRINT_NMEA_WAIT); 00205 } 00206 } 00207 00208 int main() 00209 { 00210 Ticker sample_tick, bit_tick; 00211 Thread gps_thread(gps_rx_thread); // gps receive thread 00212 Thread rtty_thread(rtty_tx_thread); // rtty transmit thread 00213 Thread print_thread(print_nmea_thread); // debug print thread 00214 Thread callsign_thread(insert_callsign_thread); // amateur callsign thread 00215 gps.baud(GPS_BAUD); // set gps bit rate 00216 gps.attach(&gps_rx_isr); // set up gps receive interrupt service routine 00217 sample_tick.attach_us(&rtty_sample_tick,1000000/AUDIO_FS); // begin generating audio 00218 bit_tick.attach_us(&rtty_bit_tick,1000000/RTTY_BAUD); // begin sending characters 00219 while(1); // idle forever 00220 }
Generated on Tue Jul 19 2022 02:24:43 by
1.7.2