-2
\$\begingroup\$

I am sampling an audio range signal with a bandwidth of 3100Hz and then applying a FFT using the DSP library example from Microchip to determine the most dominant frequency of the signal.

On the last step where I am supposed to get back the frequency with the highest energy, all I am getting are zeros.

The first step, which is sampling the analogue signal is working well as I have exported the array values into excel and plotted the graph.

The code is as follows:

void alarmFreq (void) //Detect the dominant frequency of the audio picked by the microphone
    {    
    int i = 0;
    fractional *p_real = &sigCmpx[0].real;
    fractcomplex *p_cmpx = &sigCmpx[0];
    readMic();
    for (ix_MicADCbuff=0;ix_MicADCbuff<FFT_BLOCK_LENGTH;ix_MicADCbuff++)
    {
        sigCmpx[ix_MicADCbuff].real = Float2Fract(micADCbuff[ix_MicADCbuff]);   // replace real part with ADC value
        sigCmpx[ix_MicADCbuff].imag = 0;                                        // set imaginary part with 0
    }
    /*for (ix_MicADCbuff=0;ix_MicADCbuff<FFT_BLOCK_LENGTH;ix_MicADCbuff++)
    {
        *p_real = micADCbuff[ix_MicADCbuff];   // replace real part with ADC value
        *p_real++;                                        
    }*/
    for ( i = 0; i < FFT_BLOCK_LENGTH; i++ )//The FFT function requires input data to be in the fractional fixed-point range [-0.5, +0.5]
        {                   
            *p_real = *p_real >>1 ;         //So, we shift all data samples by 1 bit to the right.
            *p_real++;                      //Should you desire to optimize this process, perform data scaling when first obtaining the time samples or within the BitReverseComplex function source code
        }                   
    p_real = &sigCmpx[(FFT_BLOCK_LENGTH/2)-1].real; //Set up pointers to convert real array to a complex array. The input array initially has all the real input samples followed by a series of zeros
    p_cmpx = &sigCmpx[FFT_BLOCK_LENGTH-1] ;                     
    for ( i = FFT_BLOCK_LENGTH; i > 0; i-- )        //Convert the Real input sample array to a Complex input sample array
        {                   
            (*p_cmpx).real = (*p_real--);   //We will simply zero out the imaginary part of each data sample
            (*p_cmpx--).imag = 0x0000;  
        }
    FFTComplexIP (LOG2_BLOCK_LENGTH, &sigCmpx[0], (fractcomplex *) __builtin_psvoffset(&twiddleFactors[0]), (int) __builtin_psvpage(&twiddleFactors[0]));// Perform FFT operation
    BitReverseComplex (LOG2_BLOCK_LENGTH, &sigCmpx[0]);// Store output samples in bit-reversed order of their addresses   
    SquareMagnitudeCplx(FFT_BLOCK_LENGTH, &sigCmpx[0], &sigCmpx[0].real);//Compute the square magnitude of the complex FFT output array so we have a Real output vector
    VectorMax(FFT_BLOCK_LENGTH/2, &sigCmpx[0].real, &peakFrequencyBin);//Find the frequency Bin ( = index into the SigCmpx[] array) that has the largest energy 
    peakFrequency = peakFrequencyBin*(AUDIO_FS/FFT_BLOCK_LENGTH); //Compute the frequency (in Hz) of the largest spectral component 
}

void readMic (void) //Sample microphone input
    {
    ADC1_ChannelSelectSet(ADC1_AI_MIC);
    ix_MicADCbuff=0;
    for(ix_MicADCbuff=0;ix_MicADCbuff<FFT_BLOCK_LENGTH;ix_MicADCbuff++)
        {
            //delay_ms(1);          //FS without waiting time 66790 Hz
            ADC1_SamplingStop();
            while(!ADC1_IsConversionComplete()){}
            micADCbuff[ix_MicADCbuff] = ADC1_Channel0ConversionResultGet();
        }
}

External variables

extern const fractcomplex twiddleFactors[FFT_BLOCK_LENGTH/2]    
__attribute__ ((space(auto_psv), aligned (FFT_BLOCK_LENGTH*2)));

fractcomplex sigCmpx[FFT_BLOCK_LENGTH] __attribute__ ((section (".ydata, data, ymemory"), 
    aligned (FFT_BLOCK_LENGTH * 2 *2))) ={0};

UPDATE:

I am now getting a value, 16640 Hz for dominant frequency.

When I export the data generated to dsPICWorks I get this:

enter image description here

Seems like the function is returning the correct value but the FFT is not working properly as I should get something else right?

\$\endgroup\$
2
\$\begingroup\$

That whole business involving p_real and p_cmpx looks extremely suspect to me.

The comment says "Set up pointers to convert real array to a complex array." But this is completely unnecessary — the complex array was already created by the for loop that immediately follows the readMic(); call. All you're doing here is scrambling your data (and zeroing out half if it).

Use your debugger to examine the complex array right before you call the FFT. If you don't see your waveform, there's something wrong.


I'm sorry, but I have to say it: This has all the earmarks of cargo cult programming — the copying-and-pasting of blocks of code without understanding what they do and when to use them. You need to slow down, take things one step at a time, and make sure you understand what each piece of code is doing for you and why you need it before moving on to the next.

\$\endgroup\$
  • \$\begingroup\$ The Float2Fract function seems to be destroying the signal. I have tried only using the pointer but when I load the data into the array using the pointer it fills the imaginary part with the next sample after the real part \$\endgroup\$ – RWeiser Oct 13 '18 at 13:52
  • \$\begingroup\$ Well, yes, that would be a problem, too. Why would you expect the ADC to produce floating-point data? \$\endgroup\$ – Dave Tweed Oct 13 '18 at 14:02
  • \$\begingroup\$ is not, that array is declared as an unsigned int. Even if I change it to float still the same. \$\endgroup\$ – RWeiser Oct 13 '18 at 14:11
  • 1
    \$\begingroup\$ In which case, you should be doing an int-to-fract conversion, right? You need to start thinking logically about what you're trying to accomplish here. Changing the type of the array isn't going to help if you're still stuffing integer data into it. \$\endgroup\$ – Dave Tweed Oct 13 '18 at 14:30
  • \$\begingroup\$ ok so I need to convert an int into a fractional type which is a value from -1 to 1 from what I could read. Then I can feed that into the FFT function. Any ideas how can I perform this conversion? \$\endgroup\$ – RWeiser Oct 13 '18 at 14:41
0
\$\begingroup\$

So I got it working! It was a data type issue. What I did was to change the ADC output data type bits from decimal to fractional and I loaded the samples directly into the array that the FFT function uses. However, I am still having strange behaviours.

The frequency bins are 66790/512 = 130Hz

Sometimes, when I pass a signal which dominant frequency is not a multiple of 130Hz, I get as a result the dominant frequency multiplied times 3. So I have added a divide by 3 instruction in case the result is too high. Luckily I only have two expected frequencies which are 520Hz and 3100 Hz so I can truncate the results, however, I would like to know what the issue is here.

Attached the updated code:

#define FFT_BLOCK_LENGTH    512      //Number of frequency points in the FFT
#define LOG2_BLOCK_LENGTH   9       //Number of "Butterfly" Stages in FFT processing should be related to FFT_BLOCK as in 2^9=512
#define AUDIO_FS            66790   //Sampling frequency of audio signal captured by the mic

int               ix_MicADCbuff;
unsigned long     alarmFrequency;
unsigned long     peakFrequency;

fractcomplex twiddleFactors[FFT_BLOCK_LENGTH/2]     /* Declare Twiddle Factor array in X-space*/
__attribute__ ((section (".xbss, bss, xmemory"), aligned (FFT_BLOCK_LENGTH*2)));

fractcomplex sigCmpx[FFT_BLOCK_LENGTH] __attribute__ ((section (".ydata, data, ymemory"), 
        aligned (FFT_BLOCK_LENGTH * 2 *2))) ={0};

void readMic(void)//Sample microphone input
{
        ADC1_ChannelSelectSet(ADC1_AI_MIC);
        ix_MicADCbuff=0;
        for(ix_MicADCbuff=0;ix_MicADCbuff<FFT_BLOCK_LENGTH;ix_MicADCbuff++)
            {
                //delay_us(162-116);          //FS without waiting time 66790 Hz
                ADC1_SamplingStop();
                while(!ADC1_IsConversionComplete()){}
                sigCmpx[ix_MicADCbuff].real = ADC1_Channel0ConversionResultGet();
            }
}
void alarmFreq(void)//Detect the dominant frequency of the audio picked by the microphone
{   
    int i = 0;
    fractional *p_real = &sigCmpx[0].real;
    fractcomplex *p_cmpx = &sigCmpx[0];
    readMic();
    for (ix_MicADCbuff=0;ix_MicADCbuff<FFT_BLOCK_LENGTH;ix_MicADCbuff++)
    {
        *p_real = sigCmpx[ix_MicADCbuff].real;   // load pointer with ADC values       
        *p_real++;      
    }
    for ( i = 0; i < FFT_BLOCK_LENGTH; i++ )//The FFT function requires input data to be in the fractional fixed-point range [-0.5, +0.5]
        {                   
            *p_real = *p_real >>1 ;         //So, we shift all data samples by 1 bit to the right.
            *p_real++;                      //Should you desire to optimize this process, perform data scaling when first obtaining the time samples or within the BitReverseComplex function source code
        }               
    p_real = &sigCmpx[(FFT_BLOCK_LENGTH/2)-1].real; //Set up pointers to convert real array to a complex array. The input array initially has all the real input samples followed by a series of zeros
    p_cmpx = &sigCmpx[FFT_BLOCK_LENGTH-1] ;                     
    for ( i = FFT_BLOCK_LENGTH; i > 0; i-- )        //Convert the Real input sample array to a Complex input sample array
        {                   
            (*p_cmpx).real = (*p_real--);   //We will simply zero out the imaginary part of each data sample
            (*p_cmpx--).imag = 0x0000;  
        }
    FFTComplexIP (LOG2_BLOCK_LENGTH, &sigCmpx[0], (fractcomplex *) __builtin_psvoffset(&twiddleFactors[0]), (int) __builtin_psvpage(&twiddleFactors[0]));// Perform FFT operation
    BitReverseComplex (LOG2_BLOCK_LENGTH, &sigCmpx[0]);// Store output samples in bit-reversed order of their addresses   
    SquareMagnitudeCplx(FFT_BLOCK_LENGTH, &sigCmpx[0], &sigCmpx[0].real);//Compute the square magnitude of the complex FFT output array so we have a Real output vector
    VectorMax(FFT_BLOCK_LENGTH/2, &sigCmpx[0].real, &peakFrequencyBin);//Find the frequency Bin ( = index into the SigCmpx[] array) that has the largest energy 
    peakFrequency = peakFrequencyBin*(AUDIO_FS/FFT_BLOCK_LENGTH); //Compute the frequency (in Hz) of the largest spectral component
    if(peakFrequency>3200)
        {
            peakFrequency=peakFrequency/3;
        }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.