
#include "arduinoFFT.h"

/*
  testprogram v/ Jens Dalsgaard Nielsen(JDN), Beer license :-)
  
  1. Program samples A0 on an Arduino (tested on an old Mega)
    512 samples
    200 Hz samplings frequency (same as 5 msec samplæing period)
    Analog A0 as input  0-1023 
  2. Sampling an A0 just with a wire inserted - should give nice 50 Hz noise :-)
  3. Let it run after some rounds press reset button and keep it down
     Now you can scroll in the terminal window and hopefully se a 50 Hz oin from 50 Hz noise
*/


const uint16_t samples = 512;  //JDN64; //This value MUST ALWAYS be a power of 2

const double samplingFrequency = 200;  // Hz

const unsigned long tPer = 5;          // == 1/samplingFrequency as usigned long
unsigned long tNext;                   // for doing tiem correct sampling


// for fft plot with stars etc
double vVal;
const double deltaV = 1;  // see use of scaleToMax

double vReal[samples];
double vImag[samples];

/* Create FFT object */
ArduinoFFT<double> FFT = ArduinoFFT<double>(vReal, vImag, samples, samplingFrequency);

#define SCL_INDEX 0x00
#define SCL_TIME 0x01
#define SCL_FREQUENCY 0x02
#define SCL_PLOT 0x03
// next added by JDN
#define SCL_FREQ_PLOT 0x10

void setup() {
  Serial.begin(115200);
  while (!Serial) {
  }

  tNext = millis() + tPer;  // configure for accurate 5 msec sampling
}

// -----------------------------------------

void loop() {
  static uint16_t arIndex = 0;
  if (tNext <= millis()) {
    tNext += tPer;
    vReal[arIndex] = analogRead(A0);
    vImag[arIndex] = 0.0;  //Imaginary part must be zeroed in case of looping to avoid wrong calculations and overflows
    arIndex++;
  }

  if (samples <= arIndex) {
 
    removeDC(vReal, samples);  // Arduino ADC is unipolar  [0->1023] so with substract DC offset/value

    FFT.windowing(FFTWindow::Hamming, FFTDirection::Forward); /* Weigh data */
    FFT.compute(FFTDirection::Forward);                       /* Compute FFT */
    FFT.complexToMagnitude();                                 /* Compute magnitudes */

    scaleToMax(vReal, (samples >> 1), 100.0);  // for star plot

    PrintVector(vReal, (samples >> 1), SCL_FREQUENCY);
    //PrintVector(vReal, (samples >> 1), SCL_FREQ_PLOT);  // plot a vertical FFT :-) with stars :-) by JDN

    //JDN AGAIN - we are doing real sampling
    delay(1000);
    arIndex = 0;  // ready to next round
    tNext = millis() + tPer;
  }
}

//-------------------------------------------------------------

void scaleToMax(double *vData, uint16_t bufferSize, double vMax)  //JDN - to remove DC offset due to unipolar ADC
{
  double vA = -1000.0;

  // find max
  for (uint16_t i = 0; i < bufferSize; i++) {
    if (vA < vData[i]) {
      vA = vData[i];
    }
  }

  vA = vMax / vA;
  //scale

  for (uint16_t i = 0; i < bufferSize; i++) {
    vData[i] *= vA;
  }
}


void removeDC(double *vData, uint16_t bufferSize)  //JDN - to remove DC offset due to unipolar ADC
{
  double sum = 0;

  for (uint16_t i = 0; i < bufferSize; i++) {
    sum += vData[i];
  }

  sum /= (double)bufferSize;

  for (uint16_t i = 0; i < bufferSize; i++) {
    vData[i] -= sum;
  }
}


void PrintVector(double *vData, uint16_t bufferSize, uint8_t scaleType) {

  for (uint16_t i = 0; i < bufferSize; i++) {

    double abscissa;
    switch (scaleType) {
      case SCL_INDEX:
        abscissa = (i * 1.0);
        break;
      case SCL_TIME:
        abscissa = ((i * 1.0) / samplingFrequency);
        break;
      case SCL_FREQUENCY:
        abscissa = ((i * 1.0 * samplingFrequency) / samples);
        break;
      case SCL_FREQ_PLOT:
        abscissa = ((i * 1.0 * samplingFrequency) / samples);
        break;
      default:;
    }
    /* Print abscissa value */

    if (scaleType != SCL_FREQ_PLOT) {
      Serial.print(vData[i], 4);
      Serial.print(" ");
      Serial.print(abscissa, 6);

      if (scaleType == SCL_FREQUENCY) {
        Serial.print(" Hz");
      }
      Serial.println();

    } else {  // JDN vertical freq plot with dots
      Serial.print(abscissa, 4);
      Serial.print("-");
      vVal = deltaV;
      while (vVal < vData[i]) {
        Serial.print("-");
        vVal += deltaV;  // hack for 10bit/1023 full range
      }
      Serial.print("*");
      Serial.println();
    }
  }
  Serial.println();
}

