Kenji Arai / Mbed OS cwdecoder_Goertzel_on_mbed-os

Dependencies:   TextLCD

main.cpp

Committer:
kenjiArai
Date:
2017-01-27
Revision:
0:166e0710d217

File content as of revision 0:166e0710d217:

/*
 * mbed program / cwdecoder
 *  using the Goertzel Algorithm
 *   tested on Nucleo-F411RE, F446RE, L476RG mbed board
 *
 *  Modified by Kenji Arai
 *      http://www.page.sannet.ne.jp/kenjia/index.html
 *      http://mbed.org/users/kenjiArai/
 *
 *      Started:  January  16th, 2017
 *      Revised:  January  27th, 2017
 *
 *  Original program: -> See refernce.txt
 *      http://skovholm.com/cwdecoder
 *      by Hjalmar Skovholm Hansen OZ1JHM
 *
 *  Reference: -> See refernce.txt
 *      https://courses.cs.washington.edu/courses/cse466/12au/
 *              calendar/Goertzel-EETimes.pdf
 *      by Kevin Banks
 *      http://archive.eetindia.co.in/www.eetindia.co.in/
 *              STATIC/DOWNLOAD/09banks.txt
 */
///////////////////////////////////////////////////////////////////////
// CW Decoder made by Hjalmar Skovholm Hansen OZ1JHM  VER 1.01       //
// Feel free to change, copy or what ever you like but respect       //
// that license is http://www.gnu.org/copyleft/gpl.html              //
// Discuss and give great ideas on                                   //
// https://groups.yahoo.com/neo/groups/oz1jhm/conversations/messages //
///////////////////////////////////////////////////////////////////////

//  Include --------------------------------------------------------------------
#include "mbed.h"
#include "configuration.h"
#include "TextLCD.h"

//  Definition -----------------------------------------------------------------
// see configuration.h

//  Object ---------------------------------------------------------------------
AnalogIn    analog(A0);
DigitalOut  myled(LED1);
DigitalOut  ad_cnv(D5);
DigitalOut  loop_trg(D6);
DigitalOut  buzzer(D7);
Serial      pc(USBTX, USBRX);
Timer       t;
Ticker      event;
I2C         i2c(PB_9, PB_8);  // SDA, SCL
TextLCD_I2C_N lcd(&i2c, ST7036_SA2, TextLCD::LCD16x2, NC, TextLCD::ST7032_3V3);

//  RAM ------------------------------------------------------------------------
double      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;

volatile bool    adc_action = false;

uint8_t     n = SAMPLE_NUM;
double      coeff;
double      Q1 = 0.0f;
double      Q2 = 0.0f;
double      sine;
double      cosine;  
double      sampling_freq = SAMPLE_RATE;
double      target_freq = TARGET_FREQ;
double      testData[SAMPLE_NUM + 10];
double      bw;

// Noise Blanker time which shall be computed so this is initial
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;

//  ROM / Constant data --------------------------------------------------------

//  Function prototypes --------------------------------------------------------
void setup(void);
void loop(void);
void docode(void);
void printascii(char);
void adc_convert(void);

//------------------------------------------------------------------------------
//  Control Program
//------------------------------------------------------------------------------
int main(){
    setup();
    double sampling_cycle = 1.0f / sampling_freq;
    event.attach(&adc_convert, sampling_cycle);
    t.start();  // start timer
    // LCD
    lcd.setContrast(0x1a);
    lcd.locate(0, 0);    // 1st line top
    //          1234567890123456
    lcd.printf(" CW Decoder     ");
    lcd.locate(0, 1);    // 2nd line top
    //          1234567890123456
    lcd.printf("       by JH1PJL");
    while(true){
        myled = !myled;
        loop();
    }
}

void setup(){   // Initial setting for goertzel calculation
    bw = sampling_freq / (double)n;
    double k = (int16_t) (0.5f + (((double)n * target_freq) / sampling_freq));
    double omega = (2.0f * PI * k) / (double)n;
    sine = sin(omega);
    cosine = cos(omega);
    coeff = 2.0f * cosine;
}

// Interruput service routine (triggered by event.attach(..,..)
void adc_convert(){
    adc_action = false;
    //if (adc_action == true){   adc_action = false;}
}

void loop(){
    loop_trg = !loop_trg;
    DEBUG("sample=, %u, ", n);
    for (int8_t index = 0; index < n; index++){
        // start sampling
        ad_cnv = 1;
        adc_action = true;
        // wait sampling
        while (adc_action == true){ ;}
        ad_cnv = 0;     
        testData[index] = analog.read() * 1024.0f;
        DEBUG("%f, ", testData[index]);
        double Q0 = coeff * Q1 - Q2 + testData[index];
        Q2 = Q1;
        Q1 = Q0; 
    }
    // only need the real part
    double magnitudeSquared = (Q1 * Q1) + (Q2 * Q2) - Q1 * Q2 * coeff;
    DEBUG(", mag, %f\r\n", magnitudeSquared);
    magnitude = sqrt(magnitudeSquared);
    Q2 = 0.0f;
    Q1 = 0.0f;
    // magnitude limit automatic
    if (magnitude > magnitudelimit_low){
        // moving average filter
        magnitudelimit =
                (magnitudelimit +((magnitude - magnitudelimit) / 6.0f));
    }
    if (magnitudelimit < magnitudelimit_low){
        magnitudelimit = magnitudelimit_low;
    }
    // check for the magnitude
    if(magnitude > magnitudelimit * 0.6f){ // 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;
            }
        }
        if (filteredstate == HIGH){  // we did end a LOW
            double 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.7f;}
            if(wpm > 45){   lacktime = 1.9f;}
            if(wpm > 50){   lacktime = 2.0f;}
            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){ 
        myled = HIGH;
        buzzer = 1;
    } else {
        myled = LOW;
        buzzer = 0;
    }
    // the end of main loop clean up
    realstatebefore = realstate;
    lasthighduration = highduration;
    filteredstatebefore = filteredstate;
}

// translate cw code to ascii
void docode(){
    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(6);   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(45);  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){
    PUTC(c);
    lcd.putc(c);
}