Close

Arduino Code for Spectrum Analyzer - No Graphics

A project log for Spectrum Analyser Code

Code for a spectrum analyzer.

agpcooperagp.cooper 10/23/2016 at 14:202 Comments

Arduino Code for Spectrum Analyzer

As the DigiSpark has no (working!) serial communications and the ADC for the Arduino UNO is the same for the ATTiny85 (I think), I have prototyped the code for an Arduino UNO. So all done except for the graphics!

Here is the code:

// Audio Spectrum Analyser
#define N 100            // Number of samples
#define SampleFreq 50000 // Average free running speed 
byte samples[N];         // Sample array
byte window[N];          // Window array


// Define various ADC prescaler
const unsigned char PS_16 = (1 << ADPS2);
const unsigned char PS_32 = (1 << ADPS2) | (1 << ADPS0);
const unsigned char PS_64 = (1 << ADPS2) | (1 << ADPS1);
const unsigned char PS_128 = (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

// Setup the serial port and pin 2
void setup() {
  int i;

  pinMode(LED_BUILTIN,OUTPUT);
  digitalWrite(LED_BUILTIN,LOW);
  
  // Setup the ADC
  pinMode(A2, INPUT);
  ADCSRA &= ~PS_128;  // remove bits set by Arduino library
  // Set prescaler 
  // ADCSRA |= PS_64; // 64 prescaler (250 kHz assuming a 16MHz clock)
  // ADCSRA |= PS_32; // 32 prescaler (500 kHz assuming a 16MHz clock)
  ADCSRA |= PS_16;    // 16 prescaler (1 MHz assuming a 16MHz clock)

  // Hamming Window
  for (i=0;i<N;i++) window[i]=(byte)((0.54-0.46*cos(2*M_PI*i/(N-1)))*255); 
  // Generate test samples
  for (i=0;i<N;i++) samples[i]=(byte)(50*sin(2*M_PI*i*4000/SampleFreq)+50*sin(2*M_PI*i*7000/SampleFreq)+127);

  Serial.begin(9600);
  Serial.println();
}


void loop() {  
  int i;
  int freq;
  float s;
  float s_prev;
  float s_prev2;
  float coeff;
  float magn;

  if (false) {
    // Capture the values to memory
    for (i=0;i<100;i++) samples[i]=(byte)(analogRead(A2)>>2);
  } else {
    // Pretend to be working
    delay(N*1000/SampleFreq);
  }
  
  // Scan frequencies
  for (freq=0;freq<=20000;freq+=1000) {
    coeff=2*cos(2*M_PI*freq/SampleFreq);
    s_prev=0;
    s_prev2=0;
    for (i=0;i<N;i++) {
      // Goertzel
      s=0.0000768935*window[i]*samples[i]+s_prev*coeff-s_prev2;
      s_prev2=s_prev;
      s_prev=s;
    }

    // Get magnitude
    magn=2*sqrt(s_prev2*s_prev2+s_prev*s_prev-coeff*s_prev*s_prev2)/N;

    Serial.print("Freq: ");
    Serial.print(freq);
    Serial.print(" Magn: ");
    Serial.println(magn);
  }
  digitalWrite(LED_BUILTIN,!digitalRead(LED_BUILTIN));
  delay(1000);
}

Here is the output:

  1. Freq: 0 Magn: 2.67
  2. Freq: 1000 Magn: 0.00
  3. Freq: 2000 Magn: 0.00
  4. Freq: 3000 Magn: 0.00
  5. Freq: 4000 Magn: 0.53
  6. Freq: 5000 Magn: 0.00
  7. Freq: 6000 Magn: 0.00
  8. Freq: 7000 Magn: 0.53
  9. Freq: 8000 Magn: 0.00
  10. Freq: 9000 Magn: 0.00
  11. Freq: 10000 Magn: 0.00
  12. Freq: 11000 Magn: 0.00
  13. Freq: 12000 Magn: 0.00
  14. Freq: 13000 Magn: 0.00
  15. Freq: 14000 Magn: 0.00
  16. Freq: 15000 Magn: 0.00
  17. Freq: 16000 Magn: 0.00
  18. Freq: 17000 Magn: 0.00
  19. Freq: 18000 Magn: 0.00
  20. Freq: 19000 Magn: 0.00
  21. Freq: 20000 Magn: 0.00

Which is correct!

Note that the magnitude calculation is not 100% accurate.

The DC component should be 2.49v rms (not 2.67) and the two frequencies (4000 Hz and 7000 Hz) should be 0.5v rms (not 0.53). The error issue is understood to be due to "phase" errors and "Window" errors.

Execution time:

So all good!

AlanX

Discussions

agp.cooper wrote 10/23/2016 at 15:30 point

Hi Eric,
Thanks. I find the art of coding is making it and keeping it simple.

Regards AlanX

  Are you sure? yes | no

Eric Hertz wrote 10/23/2016 at 15:16 point

Hah, I see it now, I think I went a bit overboard in my integerification, by removing *all* floating-point math, including sqrt and the initial calculations with sin(), etc. Yeah, that might've been a bit overboard.

My intention was to measure an unknown impedance via magnitude/phase... so the input-frequency would be known. I have no idea why I was so dead-set on speeding the thing up so much. 

The algorithm you've got here is much more intuitive than I remember ;)

  Are you sure? yes | no