CW Decoder (Morse code decoder) 1st release version. Only run on Nucleo-F446RE mbed board.
Dependencies: Array_Matrix F446_AD_DA ST7565_SPI_LCD TextLCD UIT_FFT_Real
Fork of F446_MySoundMachine by
Base on F446_MySoundMachine program created by 不韋 呂-san.
Thanks to 不韋 呂-san making fundamental part such as FFT and ADC high speed interrupt driven program.
I just combined LCD and show CW code.
main.cpp
- Committer:
- kenjiArai
- Date:
- 2017-02-05
- Revision:
- 6:5e21ac9f0550
- Parent:
- 5:503bd366fd73
File content as of revision 6:5e21ac9f0550:
/* * mbed program / cwdecoder using the FFT Algorithm * tested on Nucleo-F446RE mbed board * * Modified by Kenji Arai * http://www.page.sannet.ne.jp/kenjia/index.html * http://mbed.org/users/kenjiArai/ * * Started: Feburary 1st, 2017 * Revised: Feburary 5th, 2017 * * Reference program: * 1) 2016/10/02, Copyright (c) 2016 MIKAMI, Naoki * https://developer.mbed.org/users/MikamiUitOpen/code/F746_Spectrogram/ * 2017/01/31, Copyright (c) 2017 MIKAMI, Naoki * https://developer.mbed.org/users/MikamiUitOpen/code/F446_MySoundMachine/ * 2) http://skovholm.com/cwdecoder * by Hjalmar Skovholm Hansen OZ1JHM */ // Include -------------------------------------------------------------------- #include "mbed.h" #include "Matrix.hpp" #include "FFT_Analysis.hpp" #include "F446_ADC_Interrupt.hpp" #include "TextLCD.h" #include "ST7565_SPI_LCD.h" // Definition ----------------------------------------------------------------- #define METHOD_COLLECTION_HPP // MethodCollection.hpp will NOT use #define HIGH 1 #define LOW 0 #define MILLIS() t.read_ms() #define ONE_LINE 20 #define LINES 4 #define USE_COM //#define USE_DEBUG #ifdef USE_COM #define BAUD(x) pc.baud(x) #define GETC(x) pc.getc(x) #define PUTC(x) pc.putc(x) #define PRINTF(...) pc.printf(__VA_ARGS__) #define READABLE(x) pc.readable(x) #else #define BAUD(x) {;} #define GETC(x) {;} #define PUTC(x) {;} #define PRINTF(...) {;} #define READABLE(x) {;} #endif #ifdef USE_DEBUG #define DEBUG(...) pc.printf(__VA_ARGS__) #else #define DEBUG(...) {;} #endif using namespace Mikami; // ROM / Constant data -------------------------------------------------------- const int FS = 48000; const int N_FFT = 512; #define SLOT_750HZ 8 // 48KHz /512 * 8 = 750Hz /* Tried other conditions #if 0 const int FS = 24000; const int N_FFT = 256; #define SLOT_750HZ 8 // 24KHz /256 * 8 = 750Hz #endif #if 0 const int FS = 48000; const int N_FFT = 256; #define SLOT_750HZ 4 // 48KHz /256 * 4 = 750Hz #endif */ // RAM ------------------------------------------------------------------------ float magnitude ; int16_t magnitudelimit = 100; int16_t magnitudelimit_low = 100; int16_t realstate = LOW; int16_t realstatebefore = LOW; int16_t filteredstate = LOW; int16_t filteredstatebefore = LOW; int16_t nbtime = 2; // ms noise blanker int32_t starttimehigh; int32_t highduration; int32_t lasthighduration; int32_t hightimesavg; int32_t lowtimesavg; int32_t startttimelow; int32_t lowduration; int32_t laststarttime = 0; char code[32]; int16_t stop = LOW; int16_t wpm; uint32_t cycle = 0; Array<float> sn(N_FFT+1); Array<float> db(N_FFT/2+1); uint16_t adc_bf0[N_FFT + 8]; uint16_t adc_bf1[N_FFT + 8]; uint8_t adc_select = 0; uint16_t bf0_n = 0; uint16_t bf1_n = 0; volatile bool adc_bf0_full = false; volatile bool adc_bf1_full = false; volatile bool adc_data_full = false; uint8_t msg_lcd[LINES][36]; uint8_t num_last_line = 0; // Object --------------------------------------------------------------------- Timer t; DigitalOut myled(LED1); DigitalOut morse(PC_4); DigitalOut irq_job(D4); DigitalOut data_in(D5); DigitalOut loop_trg(D6); DigitalOut out_code(D7); Serial pc(USBTX, USBRX); FftAnalyzer fftAnalyzer(N_FFT+1, N_FFT); AdcSingle_Intr Adc_in(FS); // rs, e, d4, d5, d6, d7 TextLCD lcd(PB_0, PH_1, PC_0, PC_1, PC_2, PC_3, TextLCD::LCD20x4); // mosi, sck, reset, a0, ncs ST7565 glcd(PB_5, PB_3, PB_13, PB_14, PB_15, ST7565::AD12864SPI); // Function prototypes -------------------------------------------------------- void setup(void); void loop(void); void docode(void); void printascii(char); void adc_convert(void); float SpectrumUpdate(FftAnalyzer &analyzer, const Array<float> &sn, const Array<float> &db); //------------------------------------------------------------------------------ // Control Program //------------------------------------------------------------------------------ void AdcIsr() { irq_job = 1; if (adc_select == 0){ Adc_in.Read(adc_bf0[bf0_n]); bf0_n++; if (bf0_n >= N_FFT){ adc_bf0_full = true; adc_select = 1; bf1_n = 0; } } else { Adc_in.Read(adc_bf1[bf1_n]); bf1_n++; if (bf1_n >= N_FFT){ adc_bf1_full = true; adc_select = 0; bf0_n = 0; } } irq_job = 0; } int main() { wait(1.0); lcd.locate(0, 0); lcd.printf("CW DECODER(FFT) V0.1"); lcd.locate(0, 1); lcd.printf(" by JH1PJL Feb. 2017"); lcd.locate(0, 2); lcd.printf("Center Freq = 750Hz "); lcd.locate(0, 3); lcd.printf(" "); glcd.cls(); glcd.locate(0, 0); glcd.printf(" ----- CW DECODER -----\r\n"); glcd.printf(" "__DATE__"("__TIME__")\r\n"); glcd.printf(" Center freq. = 750Hz\r\n"); glcd.printf(" mbed Nucleo-F446RE\r\n"); glcd.printf(" Base: Demo_F446_AD_DA\r\n"); glcd.printf(" Kenji Arai / JH1PJL\r\n" ); glcd.printf(" kenjia@sannet.ne.jp "); PRINTF("\r\nCW Decoder(FFT) by JH1PJL\r\n"); printf("Sys=%u\r\n", SystemCoreClock); Adc_in.SetIntrVec(&AdcIsr); t.start(); while (true){ loop_trg = !loop_trg; data_in = 1; adc_data_full = false; while (adc_data_full == false){ if (adc_bf0_full == true){ for (int n=0; n < N_FFT; n++){ int32_t xData; xData = (int32_t)adc_bf0[n] - 0x00007fff; sn[n] = (float)xData; } adc_bf0_full = false; adc_data_full = true; } else if (adc_bf1_full == true){ for (int n=0; n < N_FFT; n++){ int32_t xData; xData = (int32_t)adc_bf1[n] - 0x00007fff; sn[n] = (float)xData; } adc_bf1_full = false; adc_data_full = true; } } data_in = 0; //magnitude = SpectrumUpdate(spectra, fftAnalyzer, sn, db); magnitude = SpectrumUpdate(fftAnalyzer, sn, db); //printf("%f\r\n", magnitude); if (magnitude > magnitudelimit_low){ // magnitude limit automatic magnitudelimit = // moving average filter (magnitudelimit +((magnitude - magnitudelimit) / 6.0f)); } if (magnitudelimit < magnitudelimit_low){ magnitudelimit = magnitudelimit_low; } // check for the magnitude if(magnitude > magnitudelimit * 0.9f){ // just to have some space up realstate = HIGH; } else { realstate = LOW; } // clean up the state with a noise blanker if (realstate != realstatebefore){ laststarttime = MILLIS(); } if ((MILLIS()-laststarttime)> nbtime){ if (realstate != filteredstate){ filteredstate = realstate; } } // durations on high and low if (filteredstate != filteredstatebefore){ if (filteredstate == HIGH){ starttimehigh = MILLIS(); lowduration = (MILLIS() - startttimelow); } if (filteredstate == LOW){ startttimelow = MILLIS(); highduration = (MILLIS() - starttimehigh); if (highduration < (2.0f *hightimesavg) || hightimesavg == 0.0f){ // now we know avg dit time ( rolling 3 avg) hightimesavg = (highduration+hightimesavg+hightimesavg) / 3.0f; } if (highduration > (5.0f * hightimesavg) ){ // if speed decrease fast .. hightimesavg = highduration+hightimesavg; } } } // now we will check which kind of baud we have - dit or dah // and what kind of pause we do have 1 - 3 or 7 pause // we think that hightimeavg = 1 bit if (filteredstate != filteredstatebefore){ stop = LOW; if (filteredstate == LOW){ //// we did end a HIGH // 0.6 filter out false dits if (highduration < (hightimesavg * 2.0f) && highduration > (hightimesavg * 0.6f)){ strcat(code,"."); DEBUG("."); } if (highduration > (hightimesavg*2) && highduration < (hightimesavg * 6.0f)){ strcat(code,"-"); DEBUG("-"); wpm = (wpm + (1200/((highduration)/3)))/2; DEBUG("<%dwpm>", wpm); } } if (filteredstate == HIGH){ // we did end a LOW float lacktime = 1; // when high speeds we have to have a little more pause // before new letter or new word if(wpm > 25){ lacktime = 1.0f;} if(wpm > 30){ lacktime = 1.2f;} if(wpm > 35){ lacktime = 1.5f;} if(wpm > 40){ lacktime = 1.8f;} if(wpm > 45){ lacktime = 2.2f;} if(wpm > 50){ lacktime = 2.5f;} if (lowduration > (hightimesavg*(2.0f * lacktime)) && lowduration < hightimesavg*(5.0f * lacktime)){ docode(); code[0] = '\0'; DEBUG("/"); } if (lowduration >= hightimesavg*(5.0f * lacktime)){ // word space docode(); code[0] = '\0'; printascii(' '); DEBUG("\r\n"); } } } // write if no more letters if ((MILLIS() - startttimelow) > (highduration * 6.0f) && stop == LOW){ docode(); code[0] = '\0'; stop = HIGH; } // we will turn on and off the LED // and the speaker if(filteredstate == HIGH){ morse = HIGH; } else { morse = LOW; } // the end of main loop clean up realstatebefore = realstate; lasthighduration = highduration; filteredstatebefore = filteredstate; //DEBUG("%d\r\n", t.read_ms()); } } float SpectrumUpdate(FftAnalyzer &analyzer, const Array<float> &sn, const Array<float> &db) { analyzer.Execute(sn, db); return (db[SLOT_750HZ] - 20) * 2; } // translate cw code to ascii void docode() { //PRINTF("decording<%s>", code); if (code[0] == '.'){ // . if (code[1] == '.'){ // .. if (code[2] == '.'){ // ... if (code[3] == '.'){ // .... if (strcmp(code,"...." ) == 0){ printascii('H'); return;} if (strcmp(code,"....." ) == 0){ printascii('5'); return;} if (strcmp(code,"....-" ) == 0){ printascii('4'); return;} } else if (code[3] == '-'){ // ...- if (code[4] == '.'){ // ...-. if (strcmp(code,"...-." ) == 0) { printascii(126); return;} if (strcmp(code,"...-.-" ) == 0) { printascii(62); return;} if (strcmp(code,"...-..-") == 0) { printascii(36); return;} } else if (code[4] == '-'){ // ...-- if (strcmp(code,"...--" ) == 0) { printascii('3'); return;} } else { if (strcmp(code,"...-" ) == 0) { printascii('V'); return;} } } else { // ... if (strcmp(code,"..." ) == 0){ printascii('S'); return;} } } else if (code[2] == '-'){ // ..- if (strcmp(code,"..-" ) == 0){ printascii('U'); return;} if (strcmp(code,"..-." ) == 0){ printascii('F'); return;} if (strcmp(code,"..---" ) == 0){ printascii('2'); return;} if (strcmp(code,"..--.." ) == 0){ printascii(63); return;} } else { // .. if (strcmp(code,".." ) == 0){ printascii('I'); return;} } } else if (code[1] == '-'){ // .- if (code[2] == '.'){ // .-. if (code[3] == '.'){ // .-.. if (strcmp(code,".-.." ) == 0){ printascii('L'); return;} if (strcmp(code,".-..." ) == 0){ printascii(95); return;} } else if (code[3] == '-'){ // .-.- if (strcmp(code,".-.-" ) == 0){ printascii(3); return;} if (strcmp(code,".-.-." ) == 0){ printascii(60); return;} if (strcmp(code,".-.-.-" ) == 0){ printascii(46); return;} } else { // .-. if (strcmp(code,".-." ) == 0){ printascii('R'); return;} } } else if (code[2] == '-'){ // .-- if (code[3] == '.'){ // .--. if (strcmp(code,".--." ) == 0){ printascii('P'); return;} if (strcmp(code,".--.-" ) == 0){ printascii('-'); return;} if (strcmp(code,".--.-." ) == 0){ printascii(64); return;} } else if (code[3] == '-'){ // .--- if (strcmp(code,".---" ) == 0){ printascii('J'); return;} if (strcmp(code,".----" ) == 0){ printascii('1'); return;} } else { // .-- if (strcmp(code,".--" ) == 0){ printascii('W'); return;} } } else { // .- if (strcmp(code,".-") == 0){ printascii('A'); return;} } } else { // . if (strcmp(code,".") == 0){ printascii('E'); return;} } } else if (code[0] == '-'){ // - if (code[1] == '.'){ // -. if (code[2] == '.'){ // -.. if (code[3] == '.'){ // -... if (strcmp(code,"-..." ) == 0){ printascii('B'); return;} if (strcmp(code,"-...." ) == 0){ printascii('6'); return;} if (strcmp(code,"-....-" ) == 0){ printascii('-'); return;} } else if (code[3] == '-'){ // -..- if (strcmp(code,"-..-" ) == 0){ printascii('X'); return;} if (strcmp(code,"-..-." ) == 0){ printascii(47); return;} } else { if (strcmp(code,"-.." ) == 0){ printascii('D'); return;} } } else if (code[2] == '-'){ // -.- if (code[3] == '.'){ // -.-. if (strcmp(code,"-.-." ) == 0){ printascii('C'); return;} if (strcmp(code,"-.-.--" ) == 0){ printascii(33); return;} } else if (code[3] == '-'){ // -.-- if (strcmp(code,"-.--" ) == 0){ printascii('Y'); return;} if (strcmp(code,"-.--." ) == 0){ printascii(40); return;} if (strcmp(code,"-.--.-" ) == 0){ printascii(41); return;} } else { // -.- if (strcmp(code,"-.-" ) == 0){ printascii('K'); return;} } } else { // -. if (strcmp(code,"-.") == 0){ printascii('N'); return;} } } else if (code[1] == '-'){ // - if (code[2] == '.'){ // --. if (strcmp(code,"--." ) == 0){ printascii('G'); return;} if (strcmp(code,"--.." ) == 0){ printascii('Z'); return;} if (strcmp(code,"--.-" ) == 0){ printascii('Q'); return;} if (strcmp(code,"--..." ) == 0){ printascii('7'); return;} if (strcmp(code,"--..--" ) == 0){ printascii(44); return;} } else if (code[2] == '-'){ // --- if (code[3] == '.'){ // ---. if (strcmp(code,"---.." ) == 0){ printascii('8'); return;} if (strcmp(code,"---." ) == 0){ printascii(4); return;} if (strcmp(code,"---..." ) == 0){ printascii(58); return;} } else if (code[3] == '-'){ // ---- if (strcmp(code,"----." ) == 0){ printascii('9'); return;} if (strcmp(code,"-----" ) == 0){ printascii('0'); return;} } else { // --- if (strcmp(code,"---" ) == 0){ printascii('O'); return;} } } else { // -- if (strcmp(code,"--") == 0){ printascii('M'); return;} } } else { // - if (strcmp(code,"-") == 0){ printascii('T'); return;} } } } void printascii(char c) { uint8_t i,j; out_code = 1; PRINTF("%c", c); if (num_last_line == ONE_LINE){ for (j = 0; j < LINES; j++){ // scroll one line for (i =0; i < ONE_LINE; i++){ msg_lcd[j][i] = msg_lcd[j+1][i]; } } for (i =0; i < ONE_LINE; i++){ // Clear last line msg_lcd[3][i] = ' '; } num_last_line = 0; for (i =0; i < 4; i++){ lcd.locate(0, i); lcd.printf("%s", &msg_lcd[i][0]); } } if (!(num_last_line == 0 && c == ' ')){ msg_lcd[3][num_last_line++] = c; lcd.locate(0, 3); lcd.printf("%s", &msg_lcd[3][0]); } out_code = 0; }