Advertisement
learnelectronics

Arduino CW Decoder

Aug 27th, 2018
9,704
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 13.91 KB | None | 0 0
  1. ///////////////////////////////////////////////////////////////////////
  2. // CW Decoder made by Hjalmar Skovholm Hansen OZ1JHM  VER 1.01       //
  3. // Feel free to change, copy or what ever you like but respect       //
  4. // that license is http://www.gnu.org/copyleft/gpl.html              //
  5. // Discuss and give great ideas on                                   //
  6. // https://groups.yahoo.com/neo/groups/oz1jhm/conversations/messages //
  7. ///////////////////////////////////////////////////////////////////////
  8.  
  9. ///////////////////////////////////////////////////////////////////////////
  10. // Read more here http://en.wikipedia.org/wiki/Goertzel_algorithm        //
  11. // if you want to know about FFT the http://www.dspguide.com/pdfbook.htm //
  12. ///////////////////////////////////////////////////////////////////////////
  13.  
  14. #include <LiquidCrystal.h>
  15.  
  16. ///////////////////////////////////////////////
  17. // select the pins used on the LCD panel      /
  18. ///////////////////////////////////////////////
  19. //  LiquidCrystal lcd(RS, E, D4, D5, D6, D7) //
  20. ///////////////////////////////////////////////
  21.  
  22. LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
  23.  
  24. const int colums = 16; /// have to be 16 or 20
  25. const int rows = 2;  /// have to be 2 or 4
  26.  
  27. int lcdindex = 0;
  28. int line1[colums];
  29. int line2[colums];
  30.  
  31. ////////////////////////////////
  32. // Define 8 specials letters  //
  33. ////////////////////////////////
  34.  
  35. byte U_umlaut[8] =   {B01010,B00000,B10001,B10001,B10001,B10001,B01110,B00000}; // 'Ü'  
  36. byte O_umlaut[8] =   {B01010,B00000,B01110,B10001,B10001,B10001,B01110,B00000}; // 'Ö'  
  37. byte A_umlaut[8] =   {B01010,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // 'Ä'    
  38. byte AE_capital[8] = {B01111,B10100,B10100,B11110,B10100,B10100,B10111,B00000}; // 'Æ'
  39. byte OE_capital[8] = {B00001,B01110,B10011,B10101,B11001,B01110,B10000,B00000}; // 'Ø'
  40. byte fullblock[8] =  {B11111,B11111,B11111,B11111,B11111,B11111,B11111,B11111};  
  41. byte AA_capital[8] = {B00100,B00000,B01110,B10001,B11111,B10001,B10001,B00000}; // 'Å'  
  42. byte emtyblock[8] =  {B00000,B00000,B00000,B00000,B00000,B00000,B00000,B00000};  
  43.  
  44. int audioInPin = A1;
  45. int audioOutPin = 10;
  46. int ledPin = 13;
  47.  
  48. float magnitude ;
  49. int magnitudelimit = 100;
  50. int magnitudelimit_low = 100;
  51. int realstate = LOW;
  52. int realstatebefore = LOW;
  53. int filteredstate = LOW;
  54. int filteredstatebefore = LOW;
  55.  
  56.  
  57. ///////////////////////////////////////////////////////////
  58. // The sampling frq will be 8928 on a 16 mhz             //
  59. // without any prescaler etc                             //
  60. // because we need the tone in the center of the bins    //
  61. // you can set the tone to 496, 558, 744 or 992          //
  62. // then n the number of samples which give the bandwidth //
  63. // can be (8928 / tone) * 1 or 2 or 3 or 4 etc           //
  64. // init is 8928/558 = 16 *4 = 64 samples                 //
  65. // try to take n = 96 or 128 ;o)                         //
  66. // 48 will give you a bandwidth around 186 hz            //
  67. // 64 will give you a bandwidth around 140 hz            //
  68. // 96 will give you a bandwidth around 94 hz             //
  69. // 128 will give you a bandwidth around 70 hz            //
  70. // BUT remember that high n take a lot of time           //
  71. // so you have to find the compromice - i use 48         //
  72. ///////////////////////////////////////////////////////////
  73.  
  74. float coeff;
  75. float Q1 = 0;
  76. float Q2 = 0;
  77. float sine;
  78. float cosine;  
  79. float sampling_freq=8928.0;
  80. float target_freq=558.0; /// adjust for your needs see above
  81. float n=48.0;  //// if you change  her please change next line also
  82. int testData[48];
  83.  
  84. //////////////////////////////
  85. // Noise Blanker time which //
  86. // shall be computed so     //
  87. // this is initial          //
  88. //////////////////////////////
  89. int nbtime = 6;  /// ms noise blanker        
  90.  
  91. long starttimehigh;
  92. long highduration;
  93. long lasthighduration;
  94. long hightimesavg;
  95. long lowtimesavg;
  96. long startttimelow;
  97. long lowduration;
  98. long laststarttime = 0;
  99.  
  100. char code[20];
  101. int stop = LOW;
  102. int wpm;
  103.  
  104.  
  105. ////////////////
  106. // init setup //
  107. ////////////////
  108. void setup() {
  109.  
  110. ////////////////////////////////////
  111. // The basic goertzel calculation //
  112. ////////////////////////////////////
  113.   int  k;
  114.   float omega;
  115.   k = (int) (0.5 + ((n * target_freq) / sampling_freq));
  116.   omega = (2.0 * PI * k) / n;
  117.   sine = sin(omega);
  118.   cosine = cos(omega);
  119.   coeff = 2.0 * cosine;
  120.  
  121. ///////////////////////////////
  122. // define special characters //
  123. ///////////////////////////////
  124.  lcd.createChar(0, U_umlaut); //     German
  125.  lcd.createChar(1, O_umlaut); //     German, Swedish
  126.  lcd.createChar(2, A_umlaut); //     German, Swedish
  127.  lcd.createChar(3, AE_capital); //   Danish, Norwegian
  128.  lcd.createChar(4, OE_capital); //   Danish, Norwegian
  129.  lcd.createChar(5, fullblock);        
  130.  lcd.createChar(6, AA_capital); //   Danish, Norwegian, Swedish
  131.  lcd.createChar(7, emtyblock);
  132.  lcd.clear();
  133.  
  134.  Serial.begin(115200);
  135.  pinMode(ledPin, OUTPUT);
  136.  lcd.begin(colums, rows);
  137.  for (int index = 0; index < colums; index++){
  138.     line1[index] = 32;
  139.   line2[index] = 32;
  140.  }          
  141.  
  142. }
  143.  
  144. ///////////////
  145. // main loop //
  146. ///////////////
  147.  void loop() {
  148.  
  149.   /////////////////////////////////////
  150.   // The basic where we get the tone //
  151.   /////////////////////////////////////
  152.  
  153.   for (char index = 0; index < n; index++)
  154.   {
  155.     testData[index] = analogRead(audioInPin);
  156.   }
  157.   for (char index = 0; index < n; index++){
  158.     float Q0;
  159.     Q0 = coeff * Q1 - Q2 + (float) testData[index];
  160.     Q2 = Q1;
  161.     Q1 = Q0;  
  162.   }
  163.   float magnitudeSquared = (Q1*Q1)+(Q2*Q2)-Q1*Q2*coeff;  // we do only need the real part //
  164.   magnitude = sqrt(magnitudeSquared);
  165.   Q2 = 0;
  166.   Q1 = 0;
  167.  
  168.   //Serial.print(magnitude); Serial.println();  //// here you can measure magnitude for setup..
  169.  
  170.   ///////////////////////////////////////////////////////////
  171.   // here we will try to set the magnitude limit automatic //
  172.   ///////////////////////////////////////////////////////////
  173.  
  174.   if (magnitude > magnitudelimit_low){
  175.     magnitudelimit = (magnitudelimit +((magnitude - magnitudelimit)/6));  /// moving average filter
  176.   }
  177.  
  178.   if (magnitudelimit < magnitudelimit_low)
  179.   magnitudelimit = magnitudelimit_low;
  180.  
  181.   ////////////////////////////////////
  182.   // now we check for the magnitude //
  183.   ////////////////////////////////////
  184.  
  185.   if(magnitude > magnitudelimit*0.6) // just to have some space up
  186.      realstate = HIGH;
  187.   else
  188.     realstate = LOW;
  189.  
  190.   /////////////////////////////////////////////////////
  191.   // here we clean up the state with a noise blanker //
  192.   /////////////////////////////////////////////////////
  193.  
  194.   if (realstate != realstatebefore){
  195.   laststarttime = millis();
  196.   }
  197.   if ((millis()-laststarttime)> nbtime){
  198.   if (realstate != filteredstate){
  199.     filteredstate = realstate;
  200.   }
  201.   }
  202.  
  203.  ////////////////////////////////////////////////////////////
  204.  // Then we do want to have some durations on high and low //
  205.  ////////////////////////////////////////////////////////////
  206.  
  207.  if (filteredstate != filteredstatebefore){
  208.   if (filteredstate == HIGH){
  209.     starttimehigh = millis();
  210.     lowduration = (millis() - startttimelow);
  211.   }
  212.  
  213.   if (filteredstate == LOW){
  214.     startttimelow = millis();
  215.     highduration = (millis() - starttimehigh);
  216.         if (highduration < (2*hightimesavg) || hightimesavg == 0){
  217.       hightimesavg = (highduration+hightimesavg+hightimesavg)/3;     // now we know avg dit time ( rolling 3 avg)
  218.     }
  219.     if (highduration > (5*hightimesavg) ){
  220.       hightimesavg = highduration+hightimesavg;     // if speed decrease fast ..
  221.     }
  222.   }
  223.   }
  224.  
  225.  ///////////////////////////////////////////////////////////////
  226.  // now we will check which kind of baud we have - dit or dah //
  227.  // and what kind of pause we do have 1 - 3 or 7 pause        //
  228.  // we think that hightimeavg = 1 bit                         //
  229.  ///////////////////////////////////////////////////////////////
  230.  
  231.  if (filteredstate != filteredstatebefore){
  232.   stop = LOW;
  233.   if (filteredstate == LOW){  //// we did end a HIGH
  234.    if (highduration < (hightimesavg*2) && highduration > (hightimesavg*0.6)){ /// 0.6 filter out false dits
  235.   strcat(code,".");
  236.   Serial.print(".");
  237.    }
  238.    if (highduration > (hightimesavg*2) && highduration < (hightimesavg*6)){
  239.   strcat(code,"-");
  240.   Serial.print("-");
  241.   wpm = (wpm + (1200/((highduration)/3)))/2;  //// the most precise we can do ;o)
  242.    }
  243.   }
  244.  
  245.    if (filteredstate == HIGH){  //// we did end a LOW
  246.    
  247.    float lacktime = 1;
  248.    if(wpm > 25)lacktime=1.0; ///  when high speeds we have to have a little more pause before new letter or new word
  249.    if(wpm > 30)lacktime=1.2;
  250.    if(wpm > 35)lacktime=1.5;
  251.    
  252.    if (lowduration > (hightimesavg*(2*lacktime)) && lowduration < hightimesavg*(5*lacktime)){ // letter space
  253.     docode();
  254.   code[0] = '\0';
  255.   Serial.print("/");
  256.    }
  257.    if (lowduration >= hightimesavg*(5*lacktime)){ // word space
  258.     docode();
  259.   code[0] = '\0';
  260.   printascii(32);
  261.   Serial.println();
  262.    }
  263.   }
  264.  }
  265.  
  266.  //////////////////////////////
  267.  // write if no more letters //
  268.  //////////////////////////////
  269.  
  270.   if ((millis() - startttimelow) > (highduration * 6) && stop == LOW){
  271.    docode();
  272.    code[0] = '\0';
  273.    stop = HIGH;
  274.   }
  275.  
  276.  /////////////////////////////////////
  277.  // we will turn on and off the LED //
  278.  // and the speaker                 //
  279.  /////////////////////////////////////
  280.  
  281.    if(filteredstate == HIGH){
  282.      digitalWrite(ledPin, HIGH);
  283.    tone(audioOutPin,target_freq);
  284.    }
  285.    else{
  286.      digitalWrite(ledPin, LOW);
  287.    noTone(audioOutPin);
  288.    }
  289.  
  290.  //////////////////////////////////
  291.  // the end of main loop clean up//
  292.  /////////////////////////////////
  293.  updateinfolinelcd();
  294.  realstatebefore = realstate;
  295.  lasthighduration = highduration;
  296.  filteredstatebefore = filteredstate;
  297.  }
  298.  
  299.  
  300. ////////////////////////////////
  301. // translate cw code to ascii //
  302. ////////////////////////////////
  303.  
  304. void docode(){
  305.     if (strcmp(code,".-") == 0) printascii(65);
  306.   if (strcmp(code,"-...") == 0) printascii(66);
  307.   if (strcmp(code,"-.-.") == 0) printascii(67);
  308.   if (strcmp(code,"-..") == 0) printascii(68);
  309.   if (strcmp(code,".") == 0) printascii(69);
  310.   if (strcmp(code,"..-.") == 0) printascii(70);
  311.   if (strcmp(code,"--.") == 0) printascii(71);
  312.   if (strcmp(code,"....") == 0) printascii(72);
  313.   if (strcmp(code,"..") == 0) printascii(73);
  314.   if (strcmp(code,".---") == 0) printascii(74);
  315.   if (strcmp(code,"-.-") == 0) printascii(75);
  316.   if (strcmp(code,".-..") == 0) printascii(76);
  317.   if (strcmp(code,"--") == 0) printascii(77);
  318.   if (strcmp(code,"-.") == 0) printascii(78);
  319.   if (strcmp(code,"---") == 0) printascii(79);
  320.   if (strcmp(code,".--.") == 0) printascii(80);
  321.   if (strcmp(code,"--.-") == 0) printascii(81);
  322.   if (strcmp(code,".-.") == 0) printascii(82);
  323.   if (strcmp(code,"...") == 0) printascii(83);
  324.   if (strcmp(code,"-") == 0) printascii(84);
  325.   if (strcmp(code,"..-") == 0) printascii(85);
  326.   if (strcmp(code,"...-") == 0) printascii(86);
  327.   if (strcmp(code,".--") == 0) printascii(87);
  328.   if (strcmp(code,"-..-") == 0) printascii(88);
  329.   if (strcmp(code,"-.--") == 0) printascii(89);
  330.   if (strcmp(code,"--..") == 0) printascii(90);
  331.  
  332.   if (strcmp(code,".----") == 0) printascii(49);
  333.   if (strcmp(code,"..---") == 0) printascii(50);
  334.   if (strcmp(code,"...--") == 0) printascii(51);
  335.   if (strcmp(code,"....-") == 0) printascii(52);
  336.   if (strcmp(code,".....") == 0) printascii(53);
  337.   if (strcmp(code,"-....") == 0) printascii(54);
  338.   if (strcmp(code,"--...") == 0) printascii(55);
  339.   if (strcmp(code,"---..") == 0) printascii(56);
  340.   if (strcmp(code,"----.") == 0) printascii(57);
  341.   if (strcmp(code,"-----") == 0) printascii(48);
  342.  
  343.   if (strcmp(code,"..--..") == 0) printascii(63);
  344.   if (strcmp(code,".-.-.-") == 0) printascii(46);
  345.   if (strcmp(code,"--..--") == 0) printascii(44);
  346.   if (strcmp(code,"-.-.--") == 0) printascii(33);
  347.   if (strcmp(code,".--.-.") == 0) printascii(64);
  348.   if (strcmp(code,"---...") == 0) printascii(58);
  349.   if (strcmp(code,"-....-") == 0) printascii(45);
  350.   if (strcmp(code,"-..-.") == 0) printascii(47);
  351.  
  352.   if (strcmp(code,"-.--.") == 0) printascii(40);
  353.   if (strcmp(code,"-.--.-") == 0) printascii(41);
  354.   if (strcmp(code,".-...") == 0) printascii(95);
  355.   if (strcmp(code,"...-..-") == 0) printascii(36);
  356.   if (strcmp(code,"...-.-") == 0) printascii(62);
  357.   if (strcmp(code,".-.-.") == 0) printascii(60);
  358.   if (strcmp(code,"...-.") == 0) printascii(126);
  359.   //////////////////
  360.   // The specials //
  361.   //////////////////
  362.   if (strcmp(code,".-.-") == 0) printascii(3);
  363.   if (strcmp(code,"---.") == 0) printascii(4);
  364.   if (strcmp(code,".--.-") == 0) printascii(6);
  365.  
  366. }
  367.  
  368. /////////////////////////////////////
  369. // print the ascii code to the lcd //
  370. // one a time so we can generate   //
  371. // special letters                 //
  372. /////////////////////////////////////
  373. void printascii(int asciinumber){
  374.  
  375. int fail = 0;
  376. 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
  377.  
  378.  if (lcdindex > colums-1){
  379.   lcdindex = 0;
  380.   if (rows==4){
  381.     for (int i = 0; i <= colums-1 ; i++){
  382.     lcd.setCursor(i,rows-3);
  383.     lcd.write(line2[i]);
  384.     line2[i]=line1[i];
  385.     }
  386.    }
  387.   for (int i = 0; i <= colums-1 ; i++){
  388.     lcd.setCursor(i+fail,rows-2);
  389.     lcd.write(line1[i]);
  390.   lcd.setCursor(i+fail,rows-1);
  391.     lcd.write(32);
  392.   }
  393.  }
  394.  line1[lcdindex]=asciinumber;
  395.  lcd.setCursor(lcdindex+fail,rows-1);
  396.  lcd.write(asciinumber);
  397.  lcdindex += 1;
  398. }
  399.  
  400. void updateinfolinelcd(){
  401. /////////////////////////////////////
  402. // here we update the upper line   //
  403. // with the speed.                 //
  404. /////////////////////////////////////
  405.  
  406.   int place;
  407.   if (rows == 4){
  408.    place = colums/2;}
  409.   else{
  410.    place = 2;
  411.   }
  412.   if (wpm<10){
  413.     lcd.setCursor((place)-2,0);
  414.     lcd.print("0");
  415.     lcd.setCursor((place)-1,0);
  416.     lcd.print(wpm);
  417.     lcd.setCursor((place),0);
  418.     lcd.print(" WPM");
  419.   }
  420.   else{
  421.     lcd.setCursor((place)-2,0);
  422.     lcd.print(wpm);
  423.     lcd.setCursor((place),0);
  424.     lcd.print(" WPM ");
  425.   }
  426.  
  427. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement