At the moment I am attempting to implement a program for finding 3 frequencies (xs = 30.1 kHz, ys = 28.3 kHz and zs = 25.9 kHz) through the use of the CMSIS pack on the STM32F411RE board. I cannot get the Complex FFT (CFFT) and complex magnitude working correctly.
In accordance with the freqeuncy bins I generate an array containing these frequencies, so that I can manually lookup which index bins the signals xs, ys and zs are on. I then use this index to look at the 3 fft outcomes (Xfft, Yfft, Zfft) to find the outcomes for these signals, but they dont match up.
I use the following order of functions:
DMA ADC Buffer: HAL_ADC_ConvHalfCpltCallback(ADC_HandleTypeDef* hadc)
Freqeuncy bins in binfreqs
Change ADC input to float Xfft
CFFT_F32: arm_cfft_f32(&arm_cfft_sR_f32_len1024, Xfft, 0, 0);
Complex Mag: arm_cmplx_mag_f32(Xfft, Xdsp, fftLen);
// ADC Stuff done via DMA, working correctly
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_ADC1_Init();
MX_USART2_UART_Init();
HAL_ADC_Start_DMA(&hadc1, adc_buffer, bufferLen); // adc_buffer needs to be an uint32_t
while (1)
{
/**
* Generate the frequencies
*/
for (int binfreqs = 0; binfreqs < fftLen; binfreqs++) // Generates the frequency bins to relate the amplitude to an actual value, rather than a bin frequency value
{
fftFreq[binfreqs] = binfreqs * binSize;
}
/*
* Find the amplitudes associated with the 3 emitter frequencies and store in an array for each axis. By default these arrays are generated with signal strength 0
* and with frequency index at 0: because of system limits these will indicate invalid values, as system range is from 10 - 60 kHz.
*/
volatile int32_t X_mag[3][4] = // x axis values: [index][frequency][signal_strength][phase]
{
{0, Xfreq, 0, 0}, // For x-freq index [0][0], frequency [0][1] associated with 1st biggest amplitude [0][2], phase [0][3]
{0, Yfreq, 0, 0}, // Ditto for y-freq
{0, Zfreq, 0, 0} // Ditto for z-freq
};
/*
* Finds the index in fftFreq corresponding to respectively X, Y and Z emitter frequencies
*/
for(int binSearch = 0; binSearch < fftLen; binSearch++)
{
if(fftFreq[binSearch] == Xfreq) // Find index for X emitter frequency
{
X_mag[0][0] = binSearch;
}
if(fftFreq[binSearch] == Yfreq) // Find index for Y emitter frequency
{
X_mag[1][0] = binSearch;
}
if(fftFreq[binSearch] == Zfreq) // Find index for Z emitter frequency
{
X_mag[2][0] = binSearch;
}
}
Signal processing
/* Signal processing algorithms --------------------------------------------------
*
* Only to be run once with fresh data from the buffer, [do not run continuous] or position / orientation data will be repeated.
* So only run once when conversionPaused
*/
if(conversionPaused)
{
/*
* Convert signal to voltage (12-bit, 4096)
*/
for (int floatVals = 0; floatVals < fftLen; floatVals++)
{
Xfft[floatVals] = (float) Xin[floatVals]; * 3.6 / 4096
}
/*
* Fourier transform
*/
arm_cfft_f32(&arm_cfft_sR_f32_len1024, Xfft, 0, 0); // Calculate complex fourier transform of x time signal, processing occurs in place
for (int fix_fft = 0 ; fix_fft < half_fftLen ; fix_fft++)
{
Xfft[fix_fft] = 2 * Xfft[fix_fft] / fftLen;
Xfft[fix_fft + half_fftLen] = 0;
}
/*
* Amplitude calculation
*/
arm_cmplx_mag_f32(Xfft, Xdsp, fftLen); // Calculate the magnitude of the fourier transform for x axis
/*
* Finds all signal strengths for allocated frequency indexes
*/
for(int strength_index = 0; strength_index < 3; strength_index++) // Loops through xyz frequencies for all 3 magnetometer axis
{
int x_temp_index = X_mag[strength_index][0]; // temp int necessary to store the strength, otherwise infinite loop?
X_mag[strength_index][2] = Xfft[x_temp_index]; // = Xfft[2*x_temp_index];
}
conversionPaused = 0;
}
} // While() end
} // Main() end
I do not know how I am to calculate the frequency bins for this combination of cfft and complex magnitude, as I would expect the even indexes of the array to hold the real values and the odd indexes of the array to hold the imaginary phase values. I reference some 1 2 3 examples but could not make out what I am doing wrong with my code.
However as per the images when applying an input signal of 30.1 kHz neither the 301 bin index or the 602 bin index holds the corresponding output expected?
301 bin index
602 bin index
EDIT:
I have since tried to implement the arm_cfft_f32 example given here. This latter example is completely broken as the external 10 kHz dataset is no longer included by default and trying to include it is not possible, as the program behaves poorly and keeps erroring about a return data type that is not even present in the first place. Thus I cannot use the example program given for this: it also appears to be 4 years out of date, so that is not surprising.
The arm_max_f32() function also proved not fruitful as it keeps homing in on the noise generated at bin 0 via using an analog generated signal. Manually setting this bin 0 to equal 0 then upsets the algorithm which starts pointing to random values that are not even the largest value present in the system.
Even when going manually through the CFFT data and magnitude it appears as if they are not working correctly. There are random noise values all over the spectrum parts, whilst the oscilloscope confirms that large outcomes should only be present at 0 Hz and the selected signal generator frequency (thus corresponding to a frequency bin).
Using CMSIS is extremely frustrating for me because of the little documentation and examples available, which is then further reduced by most of it simply not working (without major modification).
Related
I'm currently tracking the analog value of a photodetector coming into my system. The signal itself is cleaned, filtered (low pass and high pass), and amplified in hardware before coming into my system. The signal has a small amount of DC walk to it, which is giving me some trouble. I've attempted to just move the min up by 1% every 50 reads of the ADC,but it adds more noise than I'd like to my signal. Here's a snapshot of what I'm pulling in below (blue = signal, max/min average = green, red = min) The spikes in the red signal can be ignored that's something I'm doing to say when a certain condition is met.
Right now my function for tracking min is this:
//Determine is value is outside max or min
if(data > max) max = data;
if(data < min) min = data;
//Reset function to bring the bounds in every 50 cycles
if(rstCntr>=50){
rstCntr=0;
max = max/1.01;
min = min*1.01;
if(min <= 1200) min = 1200;
if(max >= 1900) max = 1900;
}
That works fine except when I do that 1% correction to make sure we are still tracking the signal it throws other functions off which rely on the average value and the min value. My objective is to determine:
On the negative slope of the signal
Data coming in is less than the average
Data coming in is 5% above the minimum
It is really #3 that is driving everything else. There is enough slack in the other two that they aren't that affected.
Any suggestions for a better way to track the max and min in real-time than what I'm doing?
EDIT: Per comment by ryyker: here is additional information and reproducible example code
Need more clearly described: I'm reading an analog signal approximately once every 2ms and determining whether that signal has crossed a threshold just above the minimum value of the analog signal. The signal has some DC walk in it which doesn't allow me to simply set the lowest value seen since power-on as the minimum value.
The question: On a reading-by-reading basis, how can I track the min of a signal that doesn't have a consistent minimum value?
int main(void) {
while (1)
{
//******************************************************************************
//** Process analog sensor data, calculate HR, and trigger solenoids
//** At some point this should probably be moved to a function call in System.c,
//** but I don't want to mess with it right now since it works (Adam 11/23/2022)
//******************************************************************************
//Read Analog Data for Sensor
data = ADC1_ReadChannel(7);
//Buffer the sensor data for peak/valley detection
for(int buf=3;buf>0;buf--){
dataBuffer[buf] = dataBuffer[buf-1];
}
dataBuffer[0] = data;
//Look for a valley
//Considered a valley is the 3 most recent data points are increasing
//This helps avoid noise in the signal
uint8_t count = 0;
for(int buf=0;buf<3;buf++) {
if(dataBuffer[buf]>dataBuffer[buf+1]) count++;
}
if(count >= 3) currentSlope = true; //if the last 3 points are increasing, we just passed a valley
else currentSlope = false; //not a valley
// Track the data stream max and min to calculate a signal average
// The signal average is used to determine when we are on the bottom end of the waveform.
if(data > max) max = data;
if(data < min) min = data;
if(rstCntr>=50){ //Make sure we are tracking the signal by moving min and max in every 200 samples
rstCntr=0;
max = max/1.01;
min = min*1.01;
if(min <= 1200) min = 1200; //average*.5; //Probably finger was removed from sensor, move back up
if(max >= 1900) max = 1900; //Need to see if this really works consistently
}
rstCntr++;
average = ((uint16_t)min+(uint16_t)max)/2;
trigger = min; //Variable is only used for debug output, resetting each time around
if(data < average &&
currentSlope == false && //falling edge of signal
data <= (((average-min)*.03)+min) && //Threshold above the min
{
FireSolenoids();
}
}
return 1;
}
EDIT2:
Here is what I'm seeing using the code posted by ryyker below. The green line is what I'm using as my threshold, which works fairly well, but you can see max and min don't track the signal.
EDIT3:
Update with edited min/max code. Not seeing it ever reach the max. Might be the window size is too small (set to 40 in this image).
EDIT4:
Just for extra clarity, I'm restating my objectives once again, hopefully to make things as clear as possible. It might be helpful to provide a bit more context around what the information is used for, so I'm doing that also.
Description:
I have an analog sensor which measures a periodic signal in the range of 0.6Hz to 2Hz. The signal's periodicity is not consistent from pulsewave to pulsewave. It varies +/- 20%. The periodic signal is used to determine the timing of when a valve is opened and closed.
Objective:
The valve needs to be opened a constant number of ms after the signal peak is reached, but the time it physically takes the valve to move is much longer than this constant number. In other words, opening the valve when the peak is detected means the valve opens too late.
Similar to 1, using the valley of the signal is also not enough time for the valve to physically open.
The periodicity of the signal varies enough that it isn't possible to use the peak-to-peak time from the previous two pulsewaves to determine when to open the valve.
I need to consistently determine a point on the negative sloped portion of the pulsewave to use as the trigger for opening the valve.
Approach:
My approach is to measure the minimum and maximum of the signal and then set a threshold above the minimum which I can use to determine the time the open the valve.
My thought is that by setting some constant percentage above the minimum will get me to a consistent location on the negative sloped which can be used to open the valve.
"On a reading-by-reading basis, how can I track the min of a signal that doesn't have a consistent minimum value?"
By putting each discrete signal sample through a moving window filter, and performing statistical operations on the window as it moves, standard deviation can be extracted (following mean and variance) which can then be combined with mean to determine the minimum allowed value for each point of a particular waveform. This assumes noise contribution is known and consistent.
The following implementation is one way to consider.
in header file or top of .c
//support for stats() function
#define WND_SZ 10;
int wnd_sz = WND_SZ;
typedef struct stat_s{
double arr[10];
double min; //mean - std_dev
double max; //mean + std_dev
double mean; //running
double variance;//running
double std_dev; //running
} stat_s;
void stats(double in, stat_s *out);
in .c (edit to change max and min)
// void stats(double in, stat_s *out)
// Used to monitor a continuous stream of sensor values.
// Accepts series of measurement values from a sensor,
// Each new input value is stored in array element [i%wnd_sz]
// where wnd_sz is the width of the sample array.
// instantaneous values for max and min as well as
// moving values of mean, variance, and standard deviation
// are derived once per input
void ISL_UTIL stats(double in, stat_s *out)
{
double sum = 0, sum1 = 0;
int j = 0;
static int i = 0;
out->arr[i%wnd_sz] = in;//array index values cycle within window size
//sum all elements of moving window array
for(j = 0; j < wnd_sz; j++)
sum += out->arr[j];
//compute mean
out->mean = sum / (double)wnd_sz;
//sum squares of diff between each element and mean
for (j = 0; j < wnd_sz; j++)
sum1 += pow((out->arr[j] - out->mean), 2);
//compute variance
out->variance = sum1 / (double)wnd_sz;
//compute standard deviation
out->std_dev = sqrt(out->variance);
//EDIT here:
//mean +/- std_dev
out->max = out->mean + out->std_dev;
out->min = out->mean - out->std_dev;
//END EDIT
//prevent overflow for long running sessions.
i = (i == 1000) ? 0 : ++i;
}
int main(void)
{
stat_s s = {0};
bool running = true;
double val = 0.0;
while(running)
{
//read one sample from some sensor
val = someSensor();
stats(val, &s);
// collect instantaneous and running data from s
// into variables here
if(some exit condition) break
}
return 0;
}
Using this code with 1000 bounded pseudo random values, mean is surrounded with traces depicting mean + std_dev and mean - std_dev As std_dev becomes smaller over time, the traces converge toward the mean signal:
Note: I used the following in my test code to produce data arrays of a signal with constant amplitude added to injected noise that diminishes in amplitude over time.
void gen_data(int samples)
{
srand(clock());
int i = 0;
int plotHandle[6] = {0};
stat_s s = {0};
double arr[5][samples];
memset(arr, 0, sizeof arr);
for(i=0; i < samples; i++)//simulate ongoing sampling of sensor
{
s.arr[i%wnd_sz] = 50 + rand()%100;
if(i<.20*samples) s.arr[i%wnd_sz] = 50 + rand()%100;
else if(i<.40*samples) s.arr[i%wnd_sz] = 50 + rand()%50;
else if(i<.60*samples) s.arr[i%wnd_sz] = 50 + rand()%25;
else if(i<.80*samples) s.arr[i%wnd_sz] = 50 + rand()%12;
else s.arr[i%wnd_sz] = 50 + rand()%6;
stats(s.arr[i%wnd_sz], &s);
arr[0][i] = s.mean;
arr[1][i] = s.variance;
arr[2][i] = s.std_dev;
arr[3][i] = s.min;
arr[4][i] = s.max;
}
//
Plotting algorithms deleted for brevity.
}
I'm trying to implement the Viola-Jones algorithm for object detection using Haar cascades (like openCV's implementation) in C, to detect faces. I writing the C code in a Vivado HLS compatible way, so I can port the the implementation to an FPGA. My main goal is to learn as much as possible, rather than just getting it to work. I would also appreciate any help with improving my question.
I basically started reading G. Bradski's Learning openCV, watched some online tutorials and got started writing the code. Sure enough its not detecting faces and I don't know why. At this point I care more about understanding my mistakes rather than beeing able to detect faces.
My Implementation Steps
I'm not sure how much detail is appropriate, but to keep it short:
Extracting Haar cascade data from haarcascade_frontalface_default.xml to C readable structures (huge arrays)
Writing a function to create an integral image of any given 8bit greyscale image of size 24x24 (same size as listed in the cascade)
Applying knowledge from this great post to make the necessary calculations
My Testing Scheme
Implementing a python script to detect faces using the openCV library with the same Haar cascade as mentioned above to create golden data, a detected face is cut out (ensuring 24x24 size) from the image and stored.
Stored images are converted to one dimensional C arrays, containing pixel values row-wise: img = {row0col0, row0col1, row1col0, row1col1, ... }
integral image is calculated and face detection applied
Result
Faces pass only 6 from 25 stages of the Haar cascade and are therefore not detected by my implementation, where I know they should have been detected since the python script with openCV and the same Haar cascade did indeed detect them.
My Code
/*
* This is detectFace.c
*/
#include <stdio.h>
#include "detectFace.h"
// define constants based on Haar cascade in use
// Each feature is made of max 3 rects
//#define FEAT_NO 1 // max no. of features (= 2912 for face_default.xml)
#define RECTS_IN_FEAT 3 // max no. of rect's per feature
//#define INTS_IN_RECT 5 // no. of int's needed to describe a rect
// each node has one feature (bijective relation) and three doubles
#define STAGE_NO 25 // no. of stages
#define NODE_NO 211 // no of nodes per stage, corresponds to FEAT_NO since each Node has always one feature in haarcascade_frontalface_default.xml
//#define ELMNT_IN_NODE 3 // no. of doubles needed to describe a node
// constants for frame size
#define WIN_WIDTH 24 // width = height =24
//int detectFace(int features[FEAT_NO][RECTS_IN_FEAT][INTS_IN_RECT], double stages[STAGE_NO][NODE_NO][ELMNT_IN_NODE], double stageThresh[STAGE_NO], int ii[24][24]){
int detectFace(
int ii[576],
int stageNum,
int stageOrga[25],
float stageThresholds[25],
float nodes[8739],
int featOrga[2913],
int rectangles[6383][5])
{
int passedStages = 0; // number of stages passed in this run
int faceDetected = 0; // turns to 1 if face is detected and to 0 if its not detected
// Debug:
int nodesUsed = 0; // number of floats out of nodes[] processed, use to skip to the unprocessed floats
int rectsUsed = 0; // number of rects processed
int droppedInStage0 = 0;
// loop through all stages
int i;
detectFace_label1:
for (i = 0; i < STAGE_NO; i++)
{
double tmp = 0.0; //variable to accumulate node-values, to then compare to stage threshold
int nodeNum = stageOrga[i]; // get number of nodes for this stage from stageOrga using stage index i
// loop through nodes inside each stage
// NOTE: it is assumed that each node maps to one corresponding feature. Ex: node[0] has feat[0) and node[1] has feat[1]
// because this is how it is written in the haarcascade_frontalface_default.xml
int j;
detectFace_label0:
for (j = 0; j < NODE_NO; j++)
{
// a node is defined by 3 values:
double nodeThresh = nodes[nodesUsed]; // the first value is the node threshold
double lValue = nodes[nodesUsed + 1]; // the second value is the left value
double rValue = nodes[nodesUsed + 2]; // the third value is the right value
int sum = 0; // contains the weighted value of rectangles in one Haar feature
// loop through rect's in a feature, some have 2 and some have 3 rect's.
// Each node always refers to one feature in a way that node0 maps to feature0 and node1 to feature1 (The XML file is build like that)
//int rectNum = featOrga[j]; // get number of rects for current feature using current node index j
int k;
detectFace_label2:
for (k = 0; k < RECTS_IN_FEAT; k++)
{
int x = 0, y = 0, width = 0, height = 0, weight = 0, coordUpL = 0, coordUpR = 0, coordDownL = 0, coordDownR = 0;
// a rect is defined by 5 values:
x = rectangles[rectsUsed][0]; // the first value is the x coordinate of the top left corner pixel
y = rectangles[rectsUsed][1]; // the second value is the y coordinate of the top left corner pixel
width = rectangles[rectsUsed][2]; // the third value is the width of the current rectangle
height = rectangles[rectsUsed][3]; // the fourth value is the height of this rectangle
weight = rectangles[rectsUsed][4]; // the fifth value is the weight of this rectangle
// calculating 1-Dim index for points of interest. Formula: index = width * row + column, assuming values are stored in row order
coordUpL = ((WIN_WIDTH * y) - WIN_WIDTH) + (x - 1);
coordUpR = coordUpL + width;
coordDownL = coordUpL + (height * WIN_WIDTH);
coordDownR = coordDownL + width;
// calculate the area sum according to Viola-Jones
//sum += (ii[x][y] + ii[x+width][y+height] - ii[x][y+height] - ii[x+width][y]) * weight;
sum += (ii[coordUpL] + ii[coordDownR] - ii[coordUpR] - ii[coordDownL]) * weight;
// Debug: counting the number of actual rectangles used
rectsUsed++; //
}
// decide whether the result of the feature calculation reaches the node threshold
if (sum < nodeThresh)
{
tmp += lValue; // add left value to tmp if node threshold was not reached
}
else
{
tmp += rValue; // // add right value to tmp if node threshold was reached
}
nodesUsed = nodesUsed + 3; // one node is processed, increase nodesUsed by number of floats needed to represent a node (3)¬
}
//######## at this point we went through each node in the current stage #######
// check if threshold of current stage was reached
if (tmp < stageThresholds[i])
{
faceDetected = 0; // if any stage threshold is not reached the operation is done and no face is present
// Debug: show in which stage the frame was dropped
printf("Face detection failed in stage %d \n", i);
//i = stageNum; // breaks out this loop, because i is supposed to stay smaller than STAGE_NO
}
else
{
passedStages++; // stage threshold is reached, therefore passedStages will count up
}
}
//######## at this point we went through all stages ###############################
//----------------------------------------------------------------------------------
// if the number of passed stages reaches the total number of stages, a face is detected
if (passedStages == stageNum)
{
faceDetected = 1; // one symbolizes that the input is a face
}
else
{
faceDetected = 0; // zero symbolizes that the input is not a face
};
return faceDetected;
}
Recently I've been trying to do FFT calculations on my STM32F4-Discovery evaluation board then send it to PC. I have looked into my problem - I think that I'm doing something wrong with FFT functions provided by manufacturer.
I'm using CMSIS-DSP libraries.
For now I've have been generating samples with code (if that works correct I'll do sampling by microphone).
I'm using arm_rfft_fast_f32 as my data are going to be floats in the future, but results I get in my output array are insane (I think) - I'm getting frequencies below 0.
number_of_samples = 512; (l_probek in code)
dt = 1/freq/number_of_samples
Here is my code
float32_t buffer_input[l_probek];
uint16_t i;
uint8_t mode;
float32_t dt;
float32_t freq;
bool DoFlag = false;
bool UBFlag = false;
uint32_t rozmiar = 4*l_probek;
union
{
float32_t f[l_probek];
uint8_t b[4*l_probek];
}data_out;
union
{
float32_t f[l_probek];
uint8_t b[4*l_probek];
}data_mag;
union
{
float32_t f;
uint8_t b[4];
}czest_rozdz;
/* Pointers ------------------------------------------------------------------*/
arm_rfft_fast_instance_f32 S;
arm_cfft_radix4_instance_f32 S_CFFT;
uint16_t output;
/* ---------------------------------------------------------------------------*/
int main(void)
{
freq = 5000;
dt = 0.000000390625;
_GPIO();
_LED();
_NVIC();
_EXTI(0);
arm_rfft_fast_init_f32(&S, l_probek);
GPIO_SetBits(GPIOD, LED_Green);
mode = 2;
//----------------- Infinite loop
while (1)
{
if(true)//(UBFlag == true)
for(i=0; i<l_probek; ++i)
{
buffer_input[i] = (float32_t) 15*sin(2*PI*freq*i*dt);
}
//Obliczanie FFT
arm_rfft_fast_f32(&S, buffer_input, data_out.f, 0);
//Obliczanie modulow
arm_cmplx_mag_f32(data_out.f, data_mag.f, l_probek);
USART_putdata(USART1, data_out.b, data_mag.b, rozmiar);
//USART_putdata(USART1, czest_rozdz.b, data_mag.b, rozmiar);
GPIO_ToggleBits(GPIOD, LED_Orange);
//mode++;
//UBFlag = false;
}
}
}
I'm using arm_rfft_fast_f32 as my data are going to be floats in the future, but results I get in my output array are insane (I think) - I'm getting frequencies below 0.
The arm_rfft_fast_f32 function does not return frequencies, but rather complex-valued coefficients computed using the Fast Fourier Transform (FFT). It is thus perfectly reasonable for those coefficients to be negative. More specifically, the expected coefficients for your single-cycle sin test tone input with an amplitude of 15 would be:
0.0, 0.0; // special case packing real-valued X[0] and X[N/2]
0.0, -3840.0; // X[1]
0.0, 0.0; // X[2]
0.0, 0.0; // X[3]
...
0.0, 0.0; // X[255]
Note that as indicated in the documentation the first two outputs correspond to the purely real coefficients X[0] and X[N/2] (you should be particularly careful about this special case in your subsequent call to arm_cmplx_mag_f32; see last point below).
The frequency of each of those frequency components are given by k*fs/N, where N is the number of samples (in your case l_probek) and fs = 1/dt is the sampling rate (in your case freq*l_probek):
X[0] -> 0*freq*l_probek/l_probek = 0
X[1] -> 1*freq*l_probek/l_probek = freq = 5000
X[2] -> 2*freq*l_probek/l_probek = 2*freq = 10000
X[3] -> 3*freq*l_probek/l_probek = 2*freq = 15000
...
Finally, due to the special packing of the first two values, you need to be careful when computing the N/2+1 magnitudes:
// General case for the magnitudes
arm_cmplx_mag_f32(data_out.f+2, data_mag.f+1, l_probek/2 - 1);
// Handle special cases
data_mag.f[0] = data_out.f[0];
data_mag.f[l_probek/2] = data_out.f[1];
As a follow-up to the above answer, which is awesome, some further clarifications which took me an age to figure out.
The frequency bins are centered on the target frequency, so for instance in the example above X[0] represents -2500Hz to 2500Hz, centered on zero, X[1] is 2500Hz to 7500Hz centered on 5000Hz and so on
It's common to interpolate frequencies within the bin by looking at the energy of the adjacent bins (see https://dspguru.com/dsp/howtos/how-to-interpolate-fft-peak/) if you do this you will need to make sure that your magnitude array is large enough for the bins + Nyquist and that the bin above Nyquist is 0, but note many interpolation techniques require the complex values (e.q. Quinn, Jacobson) so make sure you interpolate before finding the magnitudes.
The special case code above works because there is no complex component of the DC and Nyquist values and thus the magnitude is simply the real part
There is a bug in the code above however - although the imaginary parts of the DC and Nyquist components is always zero, the real part could still be negative, so you need to take the absolute value to get the magnitude:
// Handle special cases
data_mag.f[0] = fabs(data_out.f[0]);
data_mag.f[l_probek/2] = fabs(data_out.f[1]);
I have an ADC interrupt that I'd like to sample the channel (ADCBUF0) 8 times, then take the average of the samples. My code utilizes flags to jump out of the if statement. The code compiles and my variables are initialized elsewhere. Could someone please tell me why I am not receiving a value for SpeedADC???
///////Global////////////
int SpeedADCcount=0;
/////////////////////////
SpeedADCflag=1;
if(SpeedADCflag==1) //The following is meant to take a an average of the incoming ADC voltages
{
SpeedADCcount++;
for(i = SpeedADCcount; i < 16; i++)
{
while(!ADCON1bits.SAMP); //Sample Done?
ADCON1bits.SAMP=0; //Start Converting
while(!ADCON1bits.DONE); //Conversion Done? Should be on next Tcy cycle
SpeedADCarray[i] = ADCBUF0;
SpeedADCflag=0;
}
}
if(SpeedADCcount==15)
{
SpeedADC=SpeedADCarray[i]>>4;
SpeedADCcount=0;
// Re-enable the motor if it was turned off previous
if((SpeedADC>246) && Flags.RunMotor==0){RunMotor();}
/*Go through another stage of "filtering" for any analog input voltage below 1.25volts
You need to get the right downshift amount (to avoid dividing), such that 8 -> 3, 16 -> 4, etc. For 8 samples, you only need to downshift 3 (3 bits).
And you need to sum all of the values in a single value, not put them in separate array entries.
SpeedADCarray += ADCBUF0; /* accumulate in a single integer, not an array */
I'm receiving PCM data trough socket connection in packets containing 320 samples. Sample rate of sound is 8000 samples per second. I am doing with it something like this:
int size = 160 * 2;//160;
int isinverse = 1;
kiss_fft_scalar zero;
memset(&zero,0,sizeof(zero));
kiss_fft_cpx fft_in[size];
kiss_fft_cpx fft_out[size];
kiss_fft_cpx fft_reconstructed[size];
kiss_fftr_cfg fft = kiss_fftr_alloc(size*2 ,0 ,0,0);
kiss_fftr_cfg ifft = kiss_fftr_alloc(size*2,isinverse,0,0);
for (int i = 0; i < size; i++) {
fft_in[i].r = zero;
fft_in[i].i = zero;
fft_out[i].r = zero;
fft_out[i].i = zero;
fft_reconstructed[i].r = zero;
fft_reconstructed[i].i = zero;
}
// got my data through socket connection
for (int i = 0; i < size; i++) {
// samples are type of short
fft_in[i].r = samples[i];
fft_in[i].i = zero;
fft_out[i].r = zero;
fft_out[i].i = zero;
}
kiss_fftr(fft, (kiss_fft_scalar*) fft_in, fft_out);
kiss_fftri(ifft, fft_out, (kiss_fft_scalar*)fft_reconstructed);
// lets normalize samples
for (int i = 0; i < size; i++) {
short* samples = (short*) bufTmp1;
samples[i] = rint(fft_reconstructed[i].r/(size*2));
}
After that I fill OpenAL buffers and play them. Everything works just fine but I would like to do some filtering of audio between kiss_fftr and kiss_fftri. Starting point as I think for this is to convert sound from time domain to frequency domain, but I don't really understand what kind of data I'm receiving from kiss_fftr function. What information is stored in each of those complex number, what its real and imaginary part can tell me about frequency. And I don't know which frequencies are covered (what frequency span) in fft_out - which indexes corresponds to which frequencies.
I am total newbie in signal processing and Fourier transform topics.
Any help?
Before you jump in with both feet into a C implementation, get familiar with digital filters, esp FIR filters.
You can design the FIR filter using something like GNU Octave's signal toolbox. Look at the command fir1(the simplest), firls, or remez. Alternately, you might be able to design a FIR filter through a web page. A quick web search for "online fir filter design" found this (I have not used it, but it appears to use the equiripple design used in the remez or firpm command )
Try implementing your filter first with a direct convolution (without FFTs) and see if the speed is acceptable -- that is an easier path. If you need an FFT-based approach, there is a sample implementation of overlap-save in the kissfft/tools/kiss_fastfir.c file.
I will try to answer your questions directly.
// a) the real and imaginary components of the output need to be combined to calculate the amplitude at each frequency.
float ar,ai,scaling;
scaling=1.0/(float)size;
// then for each output [i] from the FFT...
ar = fft_out[i].r;
ai = fft_out[i].i;
amplitude[i] = 2.0 * sqrtf( ar*ar + ai*ai ) * scaling ;
// b) which index refers to which frequency? This can be calculated as follows. Only the first half of the FFT results are needed (assuming your 8KHz sampling rate)
for(i=1;i<(size/2);i++) freq = (float)i / (1/8000) / (float)size ;
// c) phase (range +/- PI) for each frequency is calculated like this:
phase[i] = phase = atan2(fft_out[i].i / fft_out[i].r);
What you might want to investigate is FFT fast convolution using overlap add or overlap save algorithms. You will need to expand the length of each FFT by the length of the impulse of your desired filter. This is because (1) FFT/IFFT convolution is circular, and (2) each index in the FFT array result corresponds to almost all frequencies (a Sinc shaped response), not just one (even if mostly near one), so any single bin modification will leak throughout the entire frequency response (except certain exact periodic frequencies).