arduino-libraries / ArduinoSound

66 stars 28 forks source link

ICS43432 I2S - FFT Analysis #3

Open oscgonfer opened 7 years ago

oscgonfer commented 7 years ago

Hello,

Thank you very much for this library. We are using it with an Arduino/Genuino Zero in order to develop applications for ambient noise frequency analysis, within the SmartCitizen project. We would like to perform FFT analysis on the new version of the kit and we found this library very useful.

However, we are finding some problems while trying to perform FFT analysis with sampling frequencies >30kHz and fftSizes = 64 (for 15kHz max frequency according to Nyquist Theorem). At some point during the acquisition and FFT calculation, it seems there is an Overload that provokes the execution to freeze. I am not sure of it being a memory Overload or other problem, so I give some details below of the case.

Would you kindly shed some light on this issue? Have you tried to perform tests at these frequencies levels?

My apologies for any bad explanation I might have provided, I hope it is somehow clear. Thank you beforehand for your help,

Óscar

sandeepmistry commented 7 years ago

Hi @oscgonfer,

Just for clarification, could you please share a full sketch that causes the issue?

It could be that the SAMD processor is not fast enough to process an 64 FFT at 30 kHZ ...

oscgonfer commented 7 years ago

Hello @sandeepmistry

Thank you very much for your prompt response.

We have found that one of the problems that slow down the execution is the interaction between the Serial and the I2S communication, which ends up collapsing it when both are opened after a certain time. This issue happens even without any FFTAnalysis after a certain time (only reading I2S data). However, as you pointed out in this topic, the I2S.read works without interruption at these frequencies if no Serial is opened.

Now, regardless the Serial being open or not, we have seen two issues:

So... for any of them, do you think there might be any additional set-up we are missing? (e.g. resistors? or not to use cables for the data transfer?) I paste the code below for better understanding (for the Native port).

Thanks again for your kind support,

#include <ArduinoSound.h>
#define Serial SerialUSB

long timer= millis();
bool ledState = true;

// sample rate for the input
const int sampleRate = 33000;

// size of the FFT to compute
const int fftSize = 64;

// size of the spectrum output, half of FFT size
const int spectrumSize = fftSize / 2;

// array to store spectrum output
int spectrum[spectrumSize];

// create an FFT analyzer to be used with the I2S input
FFTAnalyzer fftAnalyzer(fftSize);

void setup() {
// Open serial communications and wait for port to open:
  // A baud rate of 115200 is used instead of 9600 for a faster data rate
  // on non-native USB ports
  Serial.begin(115200);

  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, ledState);

  // setup the I2S audio input for the sample rate with 32-bits per sample
  if (!AudioInI2S.begin(sampleRate, 32)) {
    Serial.println("Failed to initialize I2S input!");
    while (1); // do nothing
  }

  // configure the I2S input as the input for the FFT analyzer
  if (!fftAnalyzer.input(AudioInI2S)) {
    Serial.println("Failed to set FFT analyzer input!");
    while (1); // do nothing
  }
}

void loop() {
  // check if a new analysis is available
  if (fftAnalyzer.available()) {
    // read the new spectrum
    fftAnalyzer.read(spectrum, spectrumSize);
    tick();
    // print out the spectrum
    //for (int i = 0; i < spectrumSize; i++) {
    //  Serial.print((i * sampleRate) / fftSize); // the starting frequency
    //  Serial.print("\t"); // 
    //  Serial.println(spectrum[i]); // the spectrum value
    //}
  }
}

void tick() {
    //digitalWrite(LED_BUILTIN, false);
  if (millis() - timer > 50) {
    ledState = !ledState;
    digitalWrite(LED_BUILTIN, ledState);
    timer = millis();
  }
}
sandeepmistry commented 7 years ago

Hi @oscgonfer,

Thank you for providing the sketch. I've been running it for about 20 minutes on my MKR1000 and the LED is still blinking. How long does it usually take for the sketch to stall?

So... for any of them, do you think there might be any additional set-up we are missing? (e.g. resistors? or not to use cables for the data transfer?)

I don't think those would cause issues. However, I would recommend using a short set of bread board wires for the I2S connection.

FWIW, I'm just testing the sketch on a MKR1000 with no I2S device connected. I have tried with the Serial monitor open (and closed briefly).

oscgonfer commented 7 years ago

Hi @sandeepmistry ,

Thanks for taking time responding to this topic. To provide you with some answers:

How long does it usually take for the sketch to stall?

It can vary, but normally it takes less than 10-15secs to stall (without Serial).

I don't think those would cause issues. However, I would recommend using a short set of bread board wires for the I2S connection.

We are currently using short cables with direct connection to the microphone (to eliminate any possible noise from the cable length or the breadboard if any). Nevertheless, the issue persists.

To summarise, we believe these two issues have no connection between them: the noise read in the Serial Data line (its source still to be identified) and the fact that after a certain time, the I2S protocol only receives zeroes while working with the FFT at high sampling frequencies, pausing the execution. It probably can be due to the processor load (although it seems the Zero should handle it properly as your MKR1000 does - although I still have to test what happens when the I2S mic is not connected).

Just as an update: we have now tried to perform the FFT.update in a discrete way: acquisition and FFT calculation for 50ms intervals separated by 50-100ms stand-by, for 1-2s total loops (calculation+stand-by+calculation+stand-by...) and it seems to work. However, we still see almost flat spectrums due to the "deltas" seen in the Serial Data because of the noise.

Thanks again,

Óscar

oscgonfer commented 7 years ago

Dear @sandeepmistry ,

We have some updates regarding the issues on the implementation of the ICS43432 on the Arduino Zero.

[Noisy acquisition] The direct connection of the SD pin of the mic to the #9 pin of the Zero has definitively improved the acquisition. Although the other pins (CLK or WS) seem to be not as critical, there might be an improvement after a new protoype is done, with them all directly connected to the board, without cables. Just for information, as it might be useful for other people, this came after reading at some other forums, and this issue can be critical at high fs with I2S. We have replicated this problem in a soldered proto board with very short cables, so it seems the main roocause might be either the use of cables or the interfaces of the connections. Withal, it has not improved the stalling issue, which we are trying to solve.

On the other hand, I would like to pose an additional question: Since, the I2SDoubleBuffer.h defines the maximum buffer size sent by the I2S at a value of 512 (I understand modifiable):

#define I2S_BUFFER_SIZE 512

I have some trouble understanding what's the maximum value of usable samples in the FFTAnalyzer.cpp:

uint8_t* newSamples = ((uint8_t*)_sampleBuffer) + newSamplesOffset; 
int samples = size / (_bitsPerSample / 8); 

Now, the actual question: does this mean that, for a _bitsPerSample = 32 and a size = 512, the I2S returns 128 usable samples, out of which every two are averaged as below?

const int32_t *src = (const int32_t*)buffer; 
int32_t* dst = (int32_t*)newSamples; 

for (int i = 0; i < samples; i += 2) { 
         *dst = *src / 2; 
         src++; 
         *dst += *src / 2; 
         src++; 
         dst++; 
} 

If so, I would like to suggest to prevent the appending of buffers into a _sampleBufferSize > samples buffer, since their sizes are not exactly matched. I think this could introduce FFT calculation errors, after introducing "deltas" in the buffers. I can prepare an example, if my understanding of the actual code is correct.

Thanks again for your time.

Óscar