Kenji Arai / Mbed OS cwdecoder_Goertzel_on_mbed-os

Dependencies:   TextLCD

Revision:
0:166e0710d217
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Fri Jan 27 04:07:01 2017 +0000
@@ -0,0 +1,384 @@
+/*
+ * 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);
+}