Kenji Arai / Mbed OS cwdecoder_Goertzel_on_mbed-os

Dependencies:   TextLCD

Files at this revision

API Documentation at this revision

Comitter:
kenjiArai
Date:
Fri Jan 27 04:07:01 2017 +0000
Commit message:
Morse code (CW) decoder program. Original source is made by Hjalmar Skovholm Hansen OZ1JHM. The program uses the Goertzel Algorithm.
;

Changed in this revision

TextLCD.lib Show annotated file Show diff for this revision Revisions of this file
configuration.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
reference.txt Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TextLCD.lib	Fri Jan 27 04:07:01 2017 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/users/wim/code/TextLCD/#111ca62e8a59
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/configuration.h	Fri Jan 27 04:07:01 2017 +0000
@@ -0,0 +1,98 @@
+/*
+ * mbed program / cwdecoder
+ *  using the Goertzel Algorithm
+ *   tested on Nucleo-F411RE, F446RE, L476RG mbed board
+ *
+ *  Copyright (c) 2016,'17 Kenji Arai / JH1PJL
+ *  http://www.page.sannet.ne.jp/kenjia/index.html
+ *  http://mbed.org/users/kenjiArai/
+ *      Started:  January  19th, 2017
+ *      Revised:  January  27th, 2017
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+///////////////////////////////////////////////////////////////////////
+// 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 //
+///////////////////////////////////////////////////////////////////////
+
+//  Definition -----------------------------------------------------------------
+//////////// Sampling Conditions ///////////////////////////////////////////////
+#define SAMPLE_RATE     8000.0f     // 8KHz
+#define TARGET_FREQ     750.0f      // 750Hz
+#define SAMPLE_NUM      100         // 100 samples
+////////////////////////////////////////////////////////////////////////////////
+
+//////////// Operating condition ///////////////////////////////////////////////
+// Use Virtual COM line to PC
+#if 1   // 1: use, 0: Not use
+#define USE_COM         // use Communication with PC(UART)
+#endif
+// Output debug information
+#if 0   // 1: use, 0: Not use
+#define USE_DEBUG
+#endif
+////////////////////////////////////////////////////////////////////////////////
+
+//////////// Fixed condition ///////////////////////////////////////////////////
+#define HIGH            1
+#define LOW             0
+#define PI              3.141592653f
+#define MILLIS()        t.read_ms()
+
+
+#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
+////////////////////////////////////////////////////////////////////////////////
+
+//------------------------------------------------------------------------------
+// Old configuration
+//------------------------------------------------------------------------------
+#if 0
+//////////// Sampling Conditions ///////////////////////////////////////////////
+#define SAMPLE_RATE     8000.0f     // 8KHz
+#define TARGET_FREQ     1000.0f     // 1KHz
+#define SAMPLE_NUM      100         // 100 samples
+////////////////////////////////////////////////////////////////////////////////
+#endif
+#if 0
+//////////// Sampling Conditions ///////////////////////////////////////////////
+#define SAMPLE_RATE     12000.0f    // 12KHz
+#define TARGET_FREQ     750.0f      // 750Hz
+#define SAMPLE_NUM      50          // 50 samples
+////////////////////////////////////////////////////////////////////////////////
+#endif
+#if 0
+//////////// Sampling Conditions ///////////////////////////////////////////////
+#define SAMPLE_RATE     48000.0f    // 48KHz
+#define TARGET_FREQ     800.0f      // 800Hz
+#define SAMPLE_NUM      100         // 100 samples
+////////////////////////////////////////////////////////////////////////////////
+#endif
+
--- /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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Fri Jan 27 04:07:01 2017 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#34c1facf42a174f47fdf9002cd8c6bf10ac41744
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/reference.txt	Fri Jan 27 04:07:01 2017 +0000
@@ -0,0 +1,620 @@
+//---------------------------------------------------------------------------------------------------------------------------------------------------
+//  http://www.skovholm.com/decoder11.ino
+//---------------------------------------------------------------------------------------------------------------------------------------------------
+
+///////////////////////////////////////////////////////////////////////
+// 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 //
+///////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// Read more here http://en.wikipedia.org/wiki/Goertzel_algorithm        //
+// if you want to know about FFT the http://www.dspguide.com/pdfbook.htm //
+///////////////////////////////////////////////////////////////////////////
+
+#include <LiquidCrystal.h>
+
+///////////////////////////////////////////////
+// select the pins used on the LCD panel      /
+///////////////////////////////////////////////
+//  LiquidCrystal lcd(RS, E, D4, D5, D6, D7) //
+///////////////////////////////////////////////
+
+LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
+
+const int colums = 20; /// have to be 16 or 20
+const int rows = 4;  /// have to be 2 or 4
+
+int lcdindex = 0;
+int line1[colums];
+int line2[colums];
+
+////////////////////////////////
+// Define 8 specials letters  //
+////////////////////////////////
+
+byte U_umlaut[8] =   {B01010,B00000,B10001,B10001,B10001,B10001,B01110,B00000}; // 'ワ'  
+byte O_umlaut[8] =   {B01010,B00000,B01110,B10001,B10001,B10001,B01110,B00000}; // 'ヨ'  
+byte A_umlaut[8] =   {B01010,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // 'ト'    
+byte AE_capital[8] = {B01111,B10100,B10100,B11110,B10100,B10100,B10111,B00000}; // 'ニ' 
+byte OE_capital[8] = {B00001,B01110,B10011,B10101,B11001,B01110,B10000,B00000}; // 'リ' 
+byte fullblock[8] =  {B11111,B11111,B11111,B11111,B11111,B11111,B11111,B11111};  
+byte AA_capital[8] = {B00100,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // 'ナ'   
+byte emtyblock[8] =  {B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000};  
+
+int audioInPin = A1;
+int audioOutPin = 10;
+int ledPin = 13;
+
+float magnitude ;
+int magnitudelimit = 100;
+int magnitudelimit_low = 100;
+int realstate = LOW;
+int realstatebefore = LOW;
+int filteredstate = LOW;
+int filteredstatebefore = LOW;
+
+
+///////////////////////////////////////////////////////////
+// The sampling frq will be 8928 on a 16 mhz             //
+// without any prescaler etc                             //
+// because we need the tone in the center of the bins    //
+// you can set the tone to 496, 558, 744 or 992          //
+// then n the number of samples which give the bandwidth //
+// can be (8928 / tone) * 1 or 2 or 3 or 4 etc           //
+// init is 8928/558 = 16 *4 = 64 samples                 //
+// try to take n = 96 or 128 ;o)                         //
+// 48 will give you a bandwidth around 186 hz            //
+// 64 will give you a bandwidth around 140 hz            //
+// 96 will give you a bandwidth around 94 hz             //
+// 128 will give you a bandwidth around 70 hz            //
+// BUT remember that high n take a lot of time           //
+// so you have to find the compromice - i use 48         //
+///////////////////////////////////////////////////////////
+
+float coeff;
+float Q1 = 0;
+float Q2 = 0;
+float sine;
+float cosine;  
+float sampling_freq=8928.0;
+float target_freq=558.0; /// adjust for your needs see above
+float n=48.0;  //// if you change  her please change next line also 
+int testData[48];
+
+//////////////////////////////
+// Noise Blanker time which //
+// shall be computed so     //
+// this is initial          //
+//////////////////////////////
+int nbtime = 6;  /// ms noise blanker         
+
+long starttimehigh;
+long highduration;
+long lasthighduration;
+long hightimesavg;
+long lowtimesavg;
+long startttimelow;
+long lowduration;
+long laststarttime = 0;
+
+char code[20];
+int stop = LOW;
+int wpm;
+
+
+////////////////
+// init setup //
+////////////////
+void setup() {
+
+////////////////////////////////////
+// The basic goertzel calculation //
+////////////////////////////////////
+  int   k;
+  float omega;
+  k = (int) (0.5 + ((n * target_freq) / sampling_freq));
+  omega = (2.0 * PI * k) / n;
+  sine = sin(omega);
+  cosine = cos(omega);
+  coeff = 2.0 * cosine;
+
+///////////////////////////////
+// define special characters //
+///////////////////////////////
+ lcd.createChar(0, U_umlaut); //     German
+ lcd.createChar(1, O_umlaut); //     German, Swedish
+ lcd.createChar(2, A_umlaut); //     German, Swedish 
+ lcd.createChar(3, AE_capital); //   Danish, Norwegian
+ lcd.createChar(4, OE_capital); //   Danish, Norwegian
+ lcd.createChar(5, fullblock);        
+ lcd.createChar(6, AA_capital); //   Danish, Norwegian, Swedish
+ lcd.createChar(7, emtyblock); 
+ lcd.clear(); 
+
+ Serial.begin(115200); 
+ pinMode(ledPin, OUTPUT);
+ lcd.begin(colums, rows); 
+ for (int index = 0; index < colums; index++){
+    line1[index] = 32;
+    line2[index] = 32;
+ }           
+  
+}
+
+///////////////
+// main loop //
+///////////////
+ void loop() {
+
+  ///////////////////////////////////// 
+  // The basic where we get the tone //
+  /////////////////////////////////////
+  
+  for (char index = 0; index < n; index++)
+  {
+    testData[index] = analogRead(audioInPin);
+  }
+  for (char index = 0; index < n; index++){
+      float Q0;
+      Q0 = coeff * Q1 - Q2 + (float) testData[index];
+      Q2 = Q1;
+      Q1 = Q0;  
+  }
+  float magnitudeSquared = (Q1*Q1)+(Q2*Q2)-Q1*Q2*coeff;  // we do only need the real part //
+  magnitude = sqrt(magnitudeSquared);
+  Q2 = 0;
+  Q1 = 0;
+
+  //Serial.print(magnitude); Serial.println();  //// here you can measure magnitude for setup..
+  
+  /////////////////////////////////////////////////////////// 
+  // here we will try to set the magnitude limit automatic //
+  ///////////////////////////////////////////////////////////
+  
+  if (magnitude > magnitudelimit_low){
+    magnitudelimit = (magnitudelimit +((magnitude - magnitudelimit)/6));  /// moving average filter
+  }
+ 
+  if (magnitudelimit < magnitudelimit_low)
+    magnitudelimit = magnitudelimit_low;
+  
+  ////////////////////////////////////
+  // now we check for the magnitude //
+  ////////////////////////////////////
+
+  if(magnitude > magnitudelimit*0.6) // just to have some space up 
+     realstate = HIGH; 
+  else
+    realstate = LOW; 
+  
+  ///////////////////////////////////////////////////// 
+  // here we clean up the state with a noise blanker //
+  /////////////////////////////////////////////////////
+ 
+  if (realstate != realstatebefore){
+    laststarttime = millis();
+  }
+  if ((millis()-laststarttime)> nbtime){
+    if (realstate != filteredstate){
+        filteredstate = realstate;
+    }
+  }
+ 
+ ////////////////////////////////////////////////////////////
+ // Then we do want to have some 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*hightimesavg) || hightimesavg == 0){
+            hightimesavg = (highduration+hightimesavg+hightimesavg)/3;     // now we know avg dit time ( rolling 3 avg)
+        }
+        if (highduration > (5*hightimesavg) ){
+            hightimesavg = highduration+hightimesavg;     // if speed decrease fast ..
+        }
+    }
+  }
+
+ ///////////////////////////////////////////////////////////////
+ // 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
+   if (highduration < (hightimesavg*2) && highduration > (hightimesavg*0.6)){ /// 0.6 filter out false dits
+    strcat(code,".");
+    Serial.print(".");
+   }
+   if (highduration > (hightimesavg*2) && highduration < (hightimesavg*6)){ 
+    strcat(code,"-");
+    Serial.print("-");
+    wpm = (wpm + (1200/((highduration)/3)))/2;  //// the most precise we can do ;o)
+   }
+  }
+ 
+   if (filteredstate == HIGH){  //// we did end a LOW
+   
+   float lacktime = 1;
+   if(wpm > 25)lacktime=1.0; ///  when high speeds we have to have a little more pause before new letter or new word 
+   if(wpm > 30)lacktime=1.2;
+   if(wpm > 35)lacktime=1.5;
+   
+   if (lowduration > (hightimesavg*(2*lacktime)) && lowduration < hightimesavg*(5*lacktime)){ // letter space
+    docode();
+    code[0] = '\0';
+    Serial.print("/");
+   }
+   if (lowduration >= hightimesavg*(5*lacktime)){ // word space
+    docode();
+    code[0] = '\0';
+    printascii(32);
+    Serial.println();
+   }
+  }
+ }
+ 
+ //////////////////////////////
+ // write if no more letters //
+ //////////////////////////////
+
+  if ((millis() - startttimelow) > (highduration * 6) && stop == LOW){
+   docode();
+   code[0] = '\0';
+   stop = HIGH;
+  }
+
+ /////////////////////////////////////
+ // we will turn on and off the LED //
+ // and the speaker                 //
+ /////////////////////////////////////
+ 
+   if(filteredstate == HIGH){ 
+     digitalWrite(ledPin, HIGH);
+     tone(audioOutPin,target_freq);
+   }
+   else{
+     digitalWrite(ledPin, LOW);
+     noTone(audioOutPin);
+   }
+ 
+ //////////////////////////////////
+ // the end of main loop clean up//
+ /////////////////////////////////
+ updateinfolinelcd();
+ realstatebefore = realstate;
+ lasthighduration = highduration;
+ filteredstatebefore = filteredstate;
+ }
+
+
+////////////////////////////////
+// translate cw code to ascii //
+////////////////////////////////
+
+void docode(){
+    if (strcmp(code,".-") == 0) printascii(65);
+    if (strcmp(code,"-...") == 0) printascii(66);
+    if (strcmp(code,"-.-.") == 0) printascii(67);
+    if (strcmp(code,"-..") == 0) printascii(68);
+    if (strcmp(code,".") == 0) printascii(69);
+    if (strcmp(code,"..-.") == 0) printascii(70);
+    if (strcmp(code,"--.") == 0) printascii(71);
+    if (strcmp(code,"....") == 0) printascii(72);
+    if (strcmp(code,"..") == 0) printascii(73);
+    if (strcmp(code,".---") == 0) printascii(74);
+    if (strcmp(code,"-.-") == 0) printascii(75);
+    if (strcmp(code,".-..") == 0) printascii(76);
+    if (strcmp(code,"--") == 0) printascii(77);
+    if (strcmp(code,"-.") == 0) printascii(78);
+    if (strcmp(code,"---") == 0) printascii(79);
+    if (strcmp(code,".--.") == 0) printascii(80);
+    if (strcmp(code,"--.-") == 0) printascii(81);
+    if (strcmp(code,".-.") == 0) printascii(82);
+    if (strcmp(code,"...") == 0) printascii(83);
+    if (strcmp(code,"-") == 0) printascii(84);
+    if (strcmp(code,"..-") == 0) printascii(85);
+    if (strcmp(code,"...-") == 0) printascii(86);
+    if (strcmp(code,".--") == 0) printascii(87);
+    if (strcmp(code,"-..-") == 0) printascii(88);
+    if (strcmp(code,"-.--") == 0) printascii(89);
+    if (strcmp(code,"--..") == 0) printascii(90);
+
+    if (strcmp(code,".----") == 0) printascii(49);
+    if (strcmp(code,"..---") == 0) printascii(50);
+    if (strcmp(code,"...--") == 0) printascii(51);
+    if (strcmp(code,"....-") == 0) printascii(52);
+    if (strcmp(code,".....") == 0) printascii(53);
+    if (strcmp(code,"-....") == 0) printascii(54);
+    if (strcmp(code,"--...") == 0) printascii(55);
+    if (strcmp(code,"---..") == 0) printascii(56);
+    if (strcmp(code,"----.") == 0) printascii(57);
+    if (strcmp(code,"-----") == 0) printascii(48);
+
+    if (strcmp(code,"..--..") == 0) printascii(63);
+    if (strcmp(code,".-.-.-") == 0) printascii(46);
+    if (strcmp(code,"--..--") == 0) printascii(44);
+    if (strcmp(code,"-.-.--") == 0) printascii(33);
+    if (strcmp(code,".--.-.") == 0) printascii(64);
+    if (strcmp(code,"---...") == 0) printascii(58);
+    if (strcmp(code,"-....-") == 0) printascii(45);
+    if (strcmp(code,"-..-.") == 0) printascii(47);
+
+    if (strcmp(code,"-.--.") == 0) printascii(40);
+    if (strcmp(code,"-.--.-") == 0) printascii(41);
+    if (strcmp(code,".-...") == 0) printascii(95);
+    if (strcmp(code,"...-..-") == 0) printascii(36);
+    if (strcmp(code,"...-.-") == 0) printascii(62);
+    if (strcmp(code,".-.-.") == 0) printascii(60);
+    if (strcmp(code,"...-.") == 0) printascii(126);
+    //////////////////
+    // The specials //
+    //////////////////
+    if (strcmp(code,".-.-") == 0) printascii(3);
+    if (strcmp(code,"---.") == 0) printascii(4);
+    if (strcmp(code,".--.-") == 0) printascii(6);
+
+}
+
+/////////////////////////////////////
+// print the ascii code to the lcd //
+// one a time so we can generate   //
+// special letters                 //
+/////////////////////////////////////
+void printascii(int asciinumber){
+
+int fail = 0;
+if (rows == 4 and colums == 16)fail = -4; /// to fix the library problem with 4*16 display http://forum.arduino.cc/index.php/topic,14604.0.html
+ 
+ if (lcdindex > colums-1){
+  lcdindex = 0;
+  if (rows==4){
+      for (int i = 0; i <= colums-1 ; i++){
+        lcd.setCursor(i,rows-3);
+        lcd.write(line2[i]);
+        line2[i]=line1[i];
+      }
+   }
+  for (int i = 0; i <= colums-1 ; i++){
+    lcd.setCursor(i+fail,rows-2);
+    lcd.write(line1[i]);
+    lcd.setCursor(i+fail,rows-1);
+    lcd.write(32);
+  }
+ }
+ line1[lcdindex]=asciinumber;
+ lcd.setCursor(lcdindex+fail,rows-1);
+ lcd.write(asciinumber);
+ lcdindex += 1;
+}
+
+void updateinfolinelcd(){
+/////////////////////////////////////
+// here we update the upper line   //
+// with the speed.                 //
+/////////////////////////////////////
+
+  int place;
+  if (rows == 4){
+   place = colums/2;}
+  else{
+   place = 2;
+  }
+    if (wpm<10){
+        lcd.setCursor((place)-2,0);
+        lcd.print("0");
+        lcd.setCursor((place)-1,0);
+        lcd.print(wpm);
+        lcd.setCursor((place),0);
+        lcd.print(" WPM");
+    }
+    else{
+        lcd.setCursor((place)-2,0);
+        lcd.print(wpm);
+        lcd.setCursor((place),0);
+        lcd.print(" WPM ");
+    }
+
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------------------------
+//  http://archive.eetindia.co.in/www.eetindia.co.in/STATIC/DOWNLOAD/09banks.txt
+//---------------------------------------------------------------------------------------------------------------------------------------------------
+// Listing 1   A Goertzel implementation
+
+#include <stdio.h>
+#include <math.h>
+
+#define FLOATING    float
+#define SAMPLE  unsigned char
+
+#define SAMPLING_RATE   8000.0  //8kHz
+#define TARGET_FREQUENCY    941.0   //941 Hz
+#define N   205 //Block size
+
+FLOATING coeff;
+FLOATING Q1;
+FLOATING Q2;
+FLOATING sine;
+FLOATING cosine;
+
+SAMPLE testData[N];
+
+/* Call this routine before every "block" (size=N) of samples. */
+void ResetGoertzel(void)
+{
+  Q2 = 0;
+  Q1 = 0;
+}
+
+/* Call this once, to precompute the constants. */
+void InitGoertzel(void)
+{
+  int   k;
+  FLOATING  floatN;
+  FLOATING  omega;
+
+  floatN = (FLOATING) N;
+  k = (int) (0.5 + ((floatN * TARGET_FREQUENCY) / SAMPLING_RATE));
+  omega = (2.0 * PI * k) / floatN;
+  sine = sin(omega);
+  cosine = cos(omega);
+  coeff = 2.0 * cosine;
+
+  printf("For SAMPLING_RATE = %f", SAMPLING_RATE);
+  printf(" N = %d", N);
+  printf(" and FREQUENCY = %f,\n", TARGET_FREQUENCY);
+  printf("k = %d and coeff = %f\n\n", k, coeff);
+
+  ResetGoertzel();
+}
+
+/* Call this routine for every sample. */
+void ProcessSample(SAMPLE sample)
+{
+  FLOATING Q0;
+  Q0 = coeff * Q1 - Q2 + (FLOATING) sample;
+  Q2 = Q1;
+  Q1 = Q0;
+}
+
+
+/* Basic Goertzel */
+/* Call this routine after every block to get the complex result. */
+void GetRealImag(FLOATING *realPart, FLOATING *imagPart)
+{
+  *realPart = (Q1 - Q2 * cosine);
+  *imagPart = (Q2 * sine);
+}
+
+/* Optimized Goertzel */
+/* Call this after every block to get the RELATIVE magnitude squared. */
+FLOATING GetMagnitudeSquared(void)
+{
+  FLOATING result;
+
+  result = Q1 * Q1 + Q2 * Q2 - Q1 * Q2 * coeff;
+  return result;
+}
+
+/*** End of Goertzel-specific code, the remainder is test code. */
+
+/* Synthesize some test data at a given frequency. */
+void Generate(FLOATING frequency)
+{
+  int   index;
+  FLOATING  step;
+
+  step = frequency * ((2.0 * PI) / SAMPLING_RATE);
+
+  /* Generate the test data */
+  for (index = 0; index < N; index++)
+  {
+    testData[index] = (SAMPLE) (100.0 * sin(index * step) + 100.0);
+  }
+}
+
+/* Demo 1 */
+void GenerateAndTest(FLOATING frequency)
+{
+  int   index;
+
+  FLOATING  magnitudeSquared;
+  FLOATING  magnitude;
+  FLOATING  real;
+  FLOATING  imag;
+
+  printf("For test frequency %f:\n", frequency);
+  Generate(frequency);
+
+  /* Process the samples */
+  for (index = 0; index < N; index++)
+  {
+    ProcessSample(testData[index]);
+  }
+
+  /* Do the "basic Goertzel" processing. */
+  GetRealImag(&real, &imag);
+
+  printf("real = %f imag = %f\n", real, imag);
+
+  magnitudeSquared = real*real + imag*imag;
+  printf("Relative magnitude squared = %f\n", magnitudeSquared);
+  magnitude = sqrt(magnitudeSquared);
+  printf("Relative magnitude = %f\n", magnitude);
+
+  /* Do the "optimized Goertzel" processing */
+  magnitudeSquared = GetMagnitudeSquared();
+  printf("Relative magnitude squared = %f\n", magnitudeSquared);
+  magnitude = sqrt(magnitudeSquared);
+  printf("Relative magnitude = %f\n\n", magnitude);
+
+  ResetGoertzel();
+}
+
+/* Demo 2 */
+void GenerateAndTest2(FLOATING frequency)
+{
+  int   index;
+
+  FLOATING  magnitudeSquared;
+  FLOATING  magnitude;
+  FLOATING  real;
+  FLOATING  imag;
+
+  printf("Freq=%7.1f   ", frequency);
+  Generate(frequency);
+
+  /* Process the samples. */
+  for (index = 0; index < N; index++)
+  {
+    ProcessSample(testData[index]);
+  }
+
+  /* Do the "standard Goertzel" processing. */
+  GetRealImag(&real, &imag);
+
+  magnitudeSquared = real*real + imag*imag;
+  printf("rel mag^2=%16.5f   ", magnitudeSquared);
+  magnitude = sqrt(magnitudeSquared);
+  printf("rel mag=%12.5f\n", magnitude);
+
+  ResetGoertzel();
+}
+
+int main(void)
+{
+  FLOATING freq;
+
+  InitGoertzel();
+
+  /* Demo 1 */
+  GenerateAndTest(TARGET_FREQUENCY - 250);
+  GenerateAndTest(TARGET_FREQUENCY);
+  GenerateAndTest(TARGET_FREQUENCY + 250);
+
+  /* Demo 2 */
+  for (freq = TARGET_FREQUENCY - 300; freq <= TARGET_FREQUENCY + 300; freq += 15)
+  {
+    GenerateAndTest2(freq);
+  }
+
+  return 0;
+}
+
+//---------------------------------------------------------------------------------------------------------------------------------------------------
+//  End of reference source code
+//---------------------------------------------------------------------------------------------------------------------------------------------------