Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Revision 0:166e0710d217, committed 2017-01-27
- 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
--- /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
+//---------------------------------------------------------------------------------------------------------------------------------------------------