1

We currently work with Arduino.

I am using the fft library of "open music labs FFT library"

My question is two things.

  1. Arduino code issues

  2. Inverse fft in Matlab (With the FFT results from Arduino)

    the following code using Arduino FFT library for FFT (fast Fourier transform)

    /*
    fft_adc_serial.pde
    guest openmusiclabs.com 7.7.14
    example sketch for testing the fft library.
    it takes in data on ADC0 (Analog0) and processes them
    with the fft. the data is sent out over the serial
    port at 115.2kb.
    */
    
    //#define LOG_OUT 1 // use the log output function
    #define FFT_N 256 // set to 256 point fft
    
    void setup() {
    Serial.begin(115200); // use the serial port
    TIMSK0 = 0; // turn off timer0 for lower jitter
    ADCSRA = 0xe5; // set the adc to free running mode
    ADMUX = 0x40; // use adc0
    DIDR0 = 0x01; // turn off the digital input for adc0
    }
    
    
    void loop() {
    while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
    while(!(ADCSRA & 0x10)); // wait for adc to be ready
    ADCSRA = 0xf5; // restart adc
    byte m = ADCL; // fetch adc data
    byte j = ADCH;
    int k = (j << 8) | m; // form into an int
    k -= 0x0200; // form into a signed int
    k <<= 6; // form into a 16b signed int
    fft_input[i] = k; // put real data into even bins
    fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response
    
    for (int i = 0 ; i < 512 ; i += 2) {
    fft_input[i] =  (fft_input[i] >> 8);
    fft_input[i+1] = -(fft_input[i+1] >> 8);
    }
    
    
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    //fft_mag_log(); // take the output of the fft
    sei();
    
    
    Serial.print("start");
    
    
    for (byte i = 0 ; i < FFT_N ; i+=2) { 
    
    if (! ((i>=20 && i<=40) || (i>=FFT_N-40 && i<=FFT_N-20)))
    {
     fft_input[i] = 0;
     fft_input[i+1] = 0;
    } 
    
    Serial.println(fft_input[i]); // send out the data
      }
     }
    }
    

matlab serial communication code

   clear all
   clc

   arduino=serial('COM22','BaudRate',115200 );

   fopen(arduino);

   data = fread(arduino, 256);

   ifft(data , 'symmetric');

   fclose(arduino); 
   delete(instrfindall);

With this code was an experiment. But it did not recover.

Perform fft_run () on the Arduino, and I'd like the inverse fft in matlab.

There are a lot of problems.

I would like to ask what to in some way.

Update

I've made changes based on SleuthEye's answer. But there is a problem.

-arduino code-

/*
fft_adc_serial.pde
guest openmusiclabs.com 7.7.14
example sketch for testing the fft library.
it takes in data on ADC0 (Analog0) and processes them
with the fft. the data is sent out over the serial
port at 115.2kb.
*/

//#define LOG_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft

#include <FFT.h> // include the library

void setup() {
  Serial.begin(115200); // use the serial port
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0
}

void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready

      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fft_input[i] = k; // put real data into even bins
      fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency response

    for (int i = 0 ; i < 512 ; i += 2) {
      fft_input[i] =  (fft_input[i] >> 8);
      fft_input[i+1] = -(fft_input[i+1] >> 8);
    }

    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    //  fft_mag_log(); // take the output of the fft
    sei();
    Serial.println("start");
    for (byte i = 0 ; i < FFT_N ; i+=2) { 
      Serial.write(fft_input[i]);   // send out the real part
      Serial.write(fft_input[i+1]); // send out the imaginary part
    }
  }
}

-matlab side-

clear all
clc

arduino=serial('COM22','BaudRate',115200 );

fopen(arduino);

header = fread(arduino, 5);   % skip "start" header
data   = fread(arduino, 512); % read actual data

% now rearrange the data
rearranged = data(1:2:end) + 1i * data(2:2:end);

recoverd = ifft(rearranged, 'symmetric');


fclose(arduino); 
delete(instrfindall);

My problem is: it deletes the filter part.

Arduino sends the data to MATLAB. 512 data from Arduino. (FFT_N- real 256 and imaginary 256.)

Not an exact recovery. Performing ifft in matlab, not the original data.

There is a problem with the data form.

This form of data seems to be a problem in communication.(arduino to matlab)

data = fread(arduino, 512); % read actual data.

But my guess. The exact reason was not found.

UPDATE

Thank you for your response.

    for (int i = 0 ; i < 512 ; i += 2) {
    fft_input[i] =  (fft_input[i] >> 8);
    fft_input[i+1] = -(fft_input[i+1] >> 8);
    }

This code has been found that it is not necessary.

    for (byte i = 0 ; i < FFT_N ; i+=2) { 
     Serial.write(fft_input[i]);   // send out the real part
     Serial.write(fft_input[i+1]); // send out the imaginary part
    }

My difficulty is, When you do this code, the part of OUTPUT is 256 REAL and 256 IMAGINARY.

but,

   header = fread(arduino, 5);    % skip "start" header
   data   = fread(arduino, 1024); % read actual data sent in binary form

   % now rearrange the data
   rearranged = (data(1:4:end) + 256*data(2:4:end)) + 1i *(data(3:4:end) +     256*data(4:4:end));

   recovered = ifft(rearranged, 'symmetric');

"SIZE * PRECISION must be less than or equal to InputBufferSize.."

Problem of the buffer size...

So try again. I had to modify the code as you said.

    /*
    fft_adc_serial.pde
    guest openmusiclabs.com 7.7.14
    example sketch for testing the fft library.
    it takes in data on ADC0 (Analog0) and processes them
    with the fft. the data is sent out over the serial
    port at 115.2kb.
    */

    //#define LOG_OUT 1 // use the log output function
    #define FFT_N 256 // set to 256 point fft

    #include <FFT.h> // include the library

    void setup() {
    Serial.begin(115200); // use the serial port
    TIMSK0 = 0; // turn off timer0 for lower jitter
    ADCSRA = 0xe5; // set the adc to free running mode
    ADMUX = 0x40; // use adc0
    DIDR0 = 0x01; // turn off the digital input for adc0
    }

    void loop() {
    while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < 512 ; i += 2) { // save 256 samples
    while(!(ADCSRA & 0x10)); // wait for adc to be ready
    ADCSRA = 0xf5; // restart adc
    byte m = ADCL; // fetch adc data
    byte j = ADCH;
    int k = (j << 8) | m; // form into an int
    k -= 0x0200; // form into a signed int
    k <<= 6; // form into a 16b signed int
    fft_input[i] = k; // put real data into even bins
    fft_input[i+1] = 0; // set odd bins to 0
    }
    fft_window(); // window the data for better frequency respons
    fft_reorder(); // reorder the data before doing the fft
    fft_run(); // process the data in the fft
    //    fft_mag_log(); // take the output of the fft
    sei();
    Serial.println("start"); // header send 
    for (byte i = 0 ; i < FFT_N ; i+=2) { 
    Serial.write(fft_input[i]);   // send out the real part
    Serial.write(fft_input[i+1]); // send out the imaginary part

      }
     }
    }

Your answer makes me busy. Makes active. good answer.

Community
  • 1
  • 1
skzlzl
  • 13
  • 5
  • 4
    What are the problems? What did not not recover? You have to be more specific about the errors that you are facing. We are not clairvoyants nor are we mindreaders. – rayryeng Jan 19 '16 at 16:21
  • 1
    @rayryeng Although I'd love to have the ability to mind read :D – GameOfThrows Jan 19 '16 at 16:30
  • @GameOfThrows you and I both my friend ;) – rayryeng Jan 19 '16 at 16:38
  • The updated version uses `Serial.println("start")` instead of `Serial.print("start")` for the header (which includes a few more bytes for the end of line, you must adjust accordingly). Also, the loop filtering out frequency bins 20-40 is an _unrecoverable operation_. You will not be able to get exactly the same original time series with that. There was indeed a few issues with my answer, see update. – SleuthEye Jan 25 '16 at 19:05
  • It is a good answer. However, there are some problems. I had updated my question. Please review – skzlzl Jan 26 '16 at 09:11

1 Answers1

0

I assume that the additional spectrum transformations are intentional and not what you find problematic. For example you shouldn't be expecting to get back the spectrum values in bins 20-40 inclusive since you are explicitly zeroing them out. Also, the code

for (int i = 0 ; i < 512 ; i += 2) {
  fft_input[i] =  (fft_input[i] >> 8);
  fft_input[i+1] = -(fft_input[i+1] >> 8);
}

is a trick to obtain the inverse transform using Arduino's forward transform. Since you start with time samples, I assume you want only the forward transform (and so do not need that part of the code).

Now, comparing with Arduino's FFT example, there are a few differences which may hint at what's going on. The first notable difference comes with what the example is sending which is the lower half of the spectrum magnitude (128 values), and is not sufficient to reconstruct the original signal. In your case you correctly commented out the fft_mag_log which should allow you to send spectrum's complex values. However as you loop over the fft bins, you are only sending every second value (thus missing all the imaginary parts).

The other thing to notice is the packaging of the data. More specifically you are sending a data header (the "start" string) which you will have to read on Matlab's receiving end otherwise it will just get mixed in your actual data.

Binary transfer

You are using Serial.println which sends your number in ASCII form, whereas you read them back with Matlab's fread which reads them assuming they are in binary form. For consitency you should send your data in binary form with Serial.write:

for (byte i = 0 ; i < FFT_N ; i+=2) { 
  Serial.write(fft_input[i]);   // send out the real part
  Serial.write(fft_input[i+1]); // send out the imaginary part
}

Then, since you are sending 256 complex values as interleaved real/imaginary parts (for a total of 512 values), you would need to read those 512 values (typically 2 bytes each, in little endian order) and rearrange the data on Matlab's side:

header = fread(arduino, 5);    % skip "start" header
data   = fread(arduino, 1024); % read actual data sent in binary form

% now rearrange the data
rearranged = (data(1:4:end) + 256*data(2:4:end)) + 1i *(data(3:4:end) + 256*data(4:4:end));

recovered = ifft(rearranged, 'symmetric');

ASCII transfer

Alternatively you can send the data with Serial.println (i.e. in plain ASCII):

for (byte i = 0 ; i < FFT_N ; i+=2) { 
  Serial.println(fft_input[i]);   // send out the real part
  Serial.println(fft_input[i+1]); // send out the imaginary part
}

and read it back in ASCII form in matlab with fscanf:

fscanf(arduino, "start"); % skip "start" header
data = fscanf(arduino, "%d");    % read actual data sent in plain ASCII form

% now rearrange the data
rearranged = data(1:2:end) + 1i * data(2:2:end);

recovered = ifft(rearranged, 'symmetric');
Community
  • 1
  • 1
SleuthEye
  • 14,379
  • 2
  • 32
  • 61