/////////////////////////////////////////////////////////////////////// // 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 /////////////////////////////////////////////// // select the pins used on the LCD panel / /////////////////////////////////////////////// // LiquidCrystal lcd(RS, E, D4, D5, D6, D7) // /////////////////////////////////////////////// LiquidCrystal lcd(2,3,4,5,6,7); const int colums = 16; /// have to be 16 or 20 const int rows = 2; /// 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 = A0; int wabun_ohbun = 11; int audioOutPin = 12; int ledPin = 13; float magnitude ; int magnitudelimit = 100; int magnitudelimit_low = 100; int realstate = LOW; int realstatebefore = LOW; int filteredstate = LOW; int filteredstatebefore = LOW; int waoh = 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 = 64.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(wabun_ohbun, INPUT); pinMode(ledPin, OUTPUT); lcd.begin(colums, rows); for (int index = 0; index < colums; index++){ line1[index] = 32; line2[index] = 32; } } /////////////// // main loop // /////////////// void loop() { waoh = digitalRead(wabun_ohbun); ///////////////////////////////////// // 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 if(waoh == HIGH){ decode(); }else{ decode_wa(); } code[0] = '\0'; Serial.print("/"); } if (lowduration >= hightimesavg*(5*lacktime)){ // word space if(waoh == HIGH){ decode(); }else{ decode_wa(); } code[0] = '\0'; printascii(32); Serial.println(); } } } ////////////////////////////// // write if no more letters // ////////////////////////////// if ((millis() - startttimelow) > (highduration * 6) && stop == LOW){ if(waoh == HIGH){ decode(); }else{ decode_wa(); } 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 decode(){ 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 "); // } //} //////////////////////////////// // translate cw code to ascii // //////////////////////////////// void decode_wa(){ // a if (strcmp(code,"--.--") == 0) printascii(177); if (strcmp(code,".-") == 0) printascii(178); if (strcmp(code,"..-") == 0) printascii(179); if (strcmp(code,"-.---") == 0) printascii(180); if (strcmp(code,".-...") == 0) printascii(181); // ka if (strcmp(code,".-..") == 0) printascii(182); if (strcmp(code,"-.-..") == 0) printascii(183); if (strcmp(code,"...-") == 0) printascii(184); if (strcmp(code,"-.--") == 0) printascii(185); if (strcmp(code,"----") == 0) printascii(186); // sa if (strcmp(code,"-.-.-") == 0) printascii(187); if (strcmp(code,"--.-.") == 0) printascii(188); if (strcmp(code,"---.-") == 0) printascii(189); if (strcmp(code,".---.") == 0) printascii(190); if (strcmp(code,"---.") == 0) printascii(191); // ta if (strcmp(code,"-.") == 0) printascii(192); if (strcmp(code,"..-.") == 0) printascii(193); if (strcmp(code,".--.") == 0) printascii(194); if (strcmp(code,".-.--") == 0) printascii(195); if (strcmp(code,"..-..") == 0) printascii(196); // na if (strcmp(code,".-.") == 0) printascii(197); if (strcmp(code,"-.-.") == 0) printascii(198); if (strcmp(code,"....") == 0) printascii(199); if (strcmp(code,"--.-") == 0) printascii(200); if (strcmp(code,"..--") == 0) printascii(201); // ha if (strcmp(code,"-...") == 0) printascii(202); if (strcmp(code,"--..-") == 0) printascii(203); if (strcmp(code,"--..") == 0) printascii(204); if (strcmp(code,".") == 0) printascii(205); if (strcmp(code,"-..") == 0) printascii(206); // ma if (strcmp(code,"-..-") == 0) printascii(207); if (strcmp(code,"..-.-") == 0) printascii(208); if (strcmp(code,"-") == 0) printascii(209); if (strcmp(code,"-...-") == 0) printascii(210); if (strcmp(code,"-..-.") == 0) printascii(211); // ya if (strcmp(code,".--") == 0) printascii(212); if (strcmp(code,"-..--") == 0) printascii(213); if (strcmp(code,"--") == 0) printascii(214); // ra if (strcmp(code,"...") == 0) printascii(215); if (strcmp(code,"--.") == 0) printascii(216); if (strcmp(code,"-.--.") == 0) printascii(217); if (strcmp(code,"---") == 0) printascii(218); if (strcmp(code,".-.-") == 0) printascii(219); // wa,wo,n if (strcmp(code,"-.-") == 0) printascii(220); if (strcmp(code,".---") == 0) printascii(166); if (strcmp(code,".-.-.") == 0) printascii(221); // dakuten,han-dakuten,cho-on,kuten if (strcmp(code,"..") == 0) printascii(222); if (strcmp(code,"..--.") == 0) printascii(223); if (strcmp(code,".--.-") == 0) printascii(176); if (strcmp(code,"..--.") == 0) printascii(164); // suuji 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); }