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;
}