c++arduinoembeddedarduino-uno

comparison logic with signed integers and noise giving unexpected outputs


I’m working on an Arduino Morse code decoder that interprets durations (positive/negative for tone on/off) into dots, dashes, and spaces. I add random noise to simulate real-world signal variance, but my comparison logic is failing to classify durations correctly — often printing "miss" even when durations should match expected ranges.

I have changed the shortest durations to be abs(durations[I]) instead of without and adjusted the noise so the max value is 1. But it does not add spaces

String abcMorse = ".- -... -.-.";
const int abcLength = 19;
int abcDurations[abcLength] = {420,-420,1260,-2940,1260,-420,420,-420,420,-420,420,-2940,1260,-420,420,-420,1260,-420,420};

void setup() {
  Serial.begin(9600);
  Serial.println("");
  randomSeed(analogRead(0));
  duration2morse(abcDurations,abcLength);
}

String duration2morse(int* durations, int length) {
  const float Variability = 0.1;
  int shortestDuration = 20000;
  addNoise(durations, length, Variability);
  for(int i=0; i < length; i++) {
    if(abs(durations[i]) < shortestDuration) {
      shortestDuration = abs(durations[i]);
    }
  }
  for(int i=0; i < length; i++) {
    if(durations[i] > shortestDuration * (1 - Variability) && durations[i] < shortestDuration * (1 + Variability)) {
      Serial.print(".");
    } else if(durations[i] > shortestDuration * (3 - Variability) && durations[i] < shortestDuration * (3 + Variability)) {
      Serial.print("-");
    } else if(abs(durations[i]) > shortestDuration * (3 - Variability) && abs(durations[i]) < shortestDuration * (3 + Variability)) {
      Serial.print("/");
    } else if(abs(durations[i]) > shortestDuration * (7 - Variability) && abs(durations[i]) < shortestDuration * (7 + Variability)) {
      Serial.print(" ");
    } else {
      Serial.println("miss");
    }
  }
}

void addNoise(int* array, int length, float fraction) {
  for(int i=0; i < length; i++) {
    long variability = random(fraction * 100);
    long plusMinus = random(0,2);
    if(plusMinus == 0) {
      array[i] = array[i] + variability ;
    } else if(plusMinus == 1) {
      array[i] = array[i] - variability;
    }
  }
}

Solution

  • The answer

    Missing call of abs()

    Originally I did not have an abs() on meaning that it was setting the the shortest duration to a negative, causing errors further down. @IgorTandetnik pointed this out.

    Old

    for(int i=0; i < length; i++) {
        if(abs(durations[i]) < shortestDuration) {
          shortestDuration = durations[i];
        }
      }
    

    New

    for(int i=0; i < length; i++) {
        if(abs(durations[i]) < shortestDuration) {
          shortestDuration = abs(durations[i]); <-- This part
        }
      }
    

    Comparison Issues

    The adding the abs() call, fixed most problems put I did not handle the remaining value properly. It would be more robust with using the median and doing the timing thresholds outside the for loop.

    Old

    for(int i=0; i < length; i++) {
        if(durations[i] > shortestDuration * (1 - Variability) && durations[i] < shortestDuration * (1 + Variability)) {
          Serial.print(".");
        } else if(durations[i] > shortestDuration * (3 - Variability) && durations[i] < shortestDuration * (3 + Variability)) {
          Serial.print("-");
        } else if(abs(durations[i]) > shortestDuration * (3 - Variability) && abs(durations[i]) < shortestDuration * (3 + Variability)) {
          Serial.print("/");
        } else if(abs(durations[i]) > shortestDuration * (7 - Variability) && abs(durations[i]) < shortestDuration * (7 + Variability)) {
          Serial.print(" ");
        } else { // <-- This does not handle the interspace between morse characters
          Serial.println("miss");
        }
      }
    

    So then I changed it, with the help of @IgorTandetnik. So that it properly handles the interspace between characters and so the variability scales well.

    New

    for(int i=0; i < length; i++) {
        if(durations[i] > shortestDuration * (1 - Variability) && durations[i] < shortestDuration * (1 + Variability)) {
          result += ".";
        } else if(durations[i] > shortestDuration * (1 - Variability)*3 && durations[i] < shortestDuration * (1 + Variability)*3) {
          result += "-";
        } else if(abs(durations[i]) > shortestDuration * (1 - Variability) && abs(durations[i]) < shortestDuration * (1 + Variability)) {
          ; // <-- This does
        } else if(abs(durations[i]) > shortestDuration * (1 - Variability)*3 && abs(durations[i]) < shortestDuration * (1 + Variability)*3) {
          result += "/";
        } else if(abs(durations[i]) > shortestDuration * (1 - Variability)*7 && abs(durations[i]) < shortestDuration * (1 + Variability)*7) {
          result += " ";
        } else {
          Serial.println("Duration to morse Error");
        }
      }
    

    Timing

    I also had a slight timing issue with random, where originally I did random(0,1) which meant that it only returned 0 as it (max - 1) which returned 0, so changing to random(2) fixes this.

    Old

    void addNoise(int* array, int length, float fraction) {
      for(int i=0; i < length; i++) {
        long variability = random(fraction * 100);
        long plusMinus = random(0,1);
        if(plusMinus == 0) {
          array[i] = array[i] + variability ;
        } else if(plusMinus == 1) {
          array[i] = array[i] - variability;
        }
      }
    }
    

    New

    void addNoise(int* array, int length, float fraction) {
      for(int i=0; i < length; i++) {
        long variability = random(fraction * 100);
        long plusMinus = random(2);
        if(plusMinus == 0) {
          array[i] = array[i] + variability ;
        } else if(plusMinus == 1) {
          array[i] = array[i] - variability;
        }
      }
    }