Kenji Arai / Mbed OS cwdecoder_Goertzel_on_mbed-os

Dependencies:   TextLCD

reference.txt

Committer:
kenjiArai
Date:
2017-01-27
Revision:
0:166e0710d217

File content as of revision 0:166e0710d217:

//---------------------------------------------------------------------------------------------------------------------------------------------------
//  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
//---------------------------------------------------------------------------------------------------------------------------------------------------