Sunday, 30 August 2015

DSP Tech Brief : Why Use A High Level Language For DSP ?

The field of Digital Signal Processing is constantly pushing the price / performance envelope of technology and traditionally this has required systems developers to use assembly language for the majority of the time critical signal processing routines. Today's commercial pressures have moved the "goal-posts" dramatically and typical project development timescales require a larger part of the application to be developed using a high level language. Another benefit to using a high level language for the system development is that a system can be rapidly prototyped to prove the algorithms and then hand optimised using assembly code for the time critical areas.


Primary Reasons For Using High Level Languages


  • High productivity
  • Portability
  • Maintainability
  • Code reuse
  • Optimising system cost / performance
  • Rapid prototyping and algorithm proving
  • Integration with real-time kernels and operating systems
  • Ease of debug
  • Availability of algorithms


The latest generation of compilers allows high level code to be compiled to a quality of assembly code that is very close to that which would be generated by hand. The development process is therefore very much easier than writing the algorithm in assembly code from scratch. An increasingly common development route is to develop the algorithms on a PC or Workstation and then rewrite the application for the target processor. Using the same language for development and deployment often allows the same code to be used for both, with the different I/O requirements handled through the use of conditional compilation of the source.


Modern high performance DSPs are also changing the way we view algorithmic efficiency and an increasing number of projects are written in a high level language because the savings at development time are far greater than the extra cost overhead of using faster processors at deployment.The architectures of the latest DSPs are also becoming more complex, for example with the integration of parallel execution units. This means that it is increasingly difficult for programmers to learn how to fully optimise their algorithms. When the complexity issue is coupled with the fact that the majority of DSP algorithms are block oriented vector processing algorithms and it is now becoming possible for high level language compilers to produce code that is 100% optimised.


If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

DSP Tech Brief : The Zoom-FFT

The Zoom-FFT is a process where an input signal is mixed down to baseband and then decimated, prior to passing it into a standard FFT. The advantage is for example that if you have a sample rate of 10 MHz and require at least 10Hz resolution over a small frequency band (say 1 KHz) then you do not need a 1 Mega point FFT, just decimate by a factor of 4096 and use a 256 point FFT which is obviously quicker.

 Advantages of the Zoom FFT are :

  • Increased frequency domain resolution
  • Reduced hardware cost and complexity
  • Wider spectral range

Applications of the Zoom FFT include :

  • Ultrasonic blood flow analysis
  • R.F. communications
  • Mechanical stress analysis
  • Doppler radar

The following diagram shows the zoom process :



While the following diagram shows the basic architecture of the Zoom-FFT :



One common question is : Is the zoom FFT the same as the chirp z-transform.

The answer is : Absolutely not. The FFT calculates the FFT at N equally spaced points around the unit circle in the z-plane, the chirp z-transform modifies the locations of these points along a contour that can lie anywhere on the z-plane. In contrast, the zoom-FFT uses digital down conversion techniques to localise the standard FFT to a narrow band of frequencies that are centered on a higher frequency. The chirp z-transform is often used to analyze signals such as speech, that have certain frequency domain charactgeristics. The zoom-FFT is used to reduce the sample rate required when analysing narrowband signals - E.G. in HF communications.

These functions, and more, are available in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

DSP Tech Brief : Signal Scrambling And Linear Feedback Shift Registers (LFSRs)

Modulated signals are often scrambled when they are modulated. There are many reasons for scrambling the signal, including : spreading the spectrum and reducing the correlation between separate channels. Most scramblers utilise a Maximum Length Pseudo-Random Binary Sequence (MLPRBS or MLS), this uses a shift register an exclusive-or gates to randomise the data, as shown in the following diagrams. There are two general arrangements for implementing a scrambler, the non-synchronising and the self-synchronising types, each has its own benefits and draw backs, the non-synchronising type requires that the transmitter and receiver are synchronised before data transmission starts however once synchronised an input bit error to the receive scrambler will cause a single bit error in the output. The self-synchronising scrambler(shown below), as the name suggests will automatically synchronise to the received bit stream but a single receive bit error will cause N+1 bit errors in the output stream, where N is the length of the shift register.



This algorithm is included in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

DSP Tech Brief : One-Pole Filters

One-pole filters are very useful and efficient techniques for filtering signals. The feedback a value can also be used to design efficient peak-hold filters in the time-domain and are also used in power spectrum analysers and can help extract features of the signals that we are analyzing.



The following equation defines the one-pole filter :

y(n) = x(n) - a * y(n-1), 0 < a < 1.0

The single coefficient defines the gain and time-constant of the filter as follows :

Time constant :



Gain :



This algorithm is included in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

DSP Tech Brief : Adaptive Filters

The most common adaptive filters are of the FIR type, particularly the Least Mean Square (LMS) adaptive filter.

The LMS filter configuration and adaptation equations are shown in the following diagrams :



The following diagram shows a plot of X^2 gainst Y^2 i.e. the adaptation surface of a 2 tap filter :



This algorithm is included in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

DSP Tech Brief : The Goertzel Algorithm

Many applications require the detection of a few discrete sinusoids. The Goertzel filter is an IIR filter that uses the feedback to generate a very high Q bandpass filter where the coefficients are easily generated from the required centre frequency, according to the following equations. The most common configuration for using this technique is to measure the signal energy before and after the filter and to compare the two. If the energies are similar then the input signal is centred in the pass-band, if the output energy is significantly lower than the input energy then the signal is outside the pass band. The Goertzel algorithm is most commonly implemented as a second order recursive IIR filter, as shown below.



This algorithm is included in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

DSP Tech Brief : Envelope Detection

A common and very efficient technique for envelope detection is based on the Hilbert Transform, (a 90 degree phase shifter) and summing the phase shifted and original signals. The Hilbert transform is typically implemented as an FIR filter so the original signal must be delayed to match the group delay of the Hilbert transform. This process can be followed by absolute and then peak hold functions, the latter is often implemented as a one pole IIR filter.



The following diagrams give an idea of how this envelope approximation process works.



The following equation shows how the coefficients of the FIR filter implementation of the Hilbert transform are calculated.



The one pole IIR filter is defined by the following equation :

y(n) = x(n) + a * y(n-1), where 0 < a < 1.0

Another option for detecting the envelope is to use the square root of the energies of the original and the Hilbert transformed signals, as follows :

Envelope = sqrt(Hilbert^2 + signal^2)

In general, this will give similar results to the absolute value technique but can be more run-time efficient.

This algorithm is included in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

Friday, 26 June 2015

Socket API C Programs That Compile For Both UNIX/Linux and Windows

This web page is a good starting point for anyone who wants to learn how to program with the UNIX socket API :
http://www.cs.dartmouth.edu/~campbell/cs60/socketprogramming.html

Many of the IP connected applications I have written need to support both Windows and Unix/Linux. There are some minor differences between the two socket APIs but these are primarily related to configuration.

Once the code has been configured the vast majority of the code will be the same for both operating systems.

These programs use the following predefined macros to select between Windows and Unix/Linux :

#if (_WIN32) || (_WIN64)

Here are versions of the code included in the above URL, that compile on both Windows and Linux.

echoServer.c


#if (_WIN32) || (_WIN64)
#undef UNICODE
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define MAXLINE 4096 /*max text line length*/
#define SERV_PORT 3000 /*port*/
#define LISTENQ 8 /*maximum number of client connections */

int main (int argc, char **argv)
{
 int n;
 socklen_t clilen;
 char buf[MAXLINE];
#if (_WIN32) || (_WIN64)
 SOCKET listenfd, connfd;
 WSADATA wsaData;
 int iResult;
#else
 int listenfd, connfd;
#endif

 struct sockaddr_in cliaddr, servaddr;

#if (_WIN32) || (_WIN64)
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
#endif

 //creation of the socket
 listenfd = socket (AF_INET, SOCK_STREAM, 0);

 //preparation of the socket address 
 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 servaddr.sin_port = htons(SERV_PORT);

 bind (listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

 listen (listenfd, LISTENQ);

 printf("%s\n","Server running...waiting for connections.");

 for ( ; ; ) {

  clilen = sizeof(cliaddr);
  connfd = accept (listenfd, (struct sockaddr *) &cliaddr, &clilen);
  printf("%s\n","Received request...");

  while ( (n = recv(connfd, buf, MAXLINE,0)) > 0)  {
   printf("%s","String received from and resent to the client:");
   puts(buf);
   send(connfd, buf, n, 0);
  }

 if (n < 0) {
  perror("Read error"); 
  exit(1);
 }
 close(connfd);

 }
 //close listening socket
 close (listenfd);

#if (_WIN32) || (_WIN64)
 WSACleanup();
#endif
}

echoClient.c


#if (_WIN32) || (_WIN64)
#undef UNICODE
#define WIN32_LEAN_AND_MEAN

#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#endif

#include <stdlib.h>
#include <stdio.h>
#include <string.h>


#define MAXLINE 4096 /*max text line length*/
#define SERV_PORT 3000 /*port*/

int
main(int argc, char **argv) 
{
 struct sockaddr_in servaddr;
 char sendline[MAXLINE], recvline[MAXLINE];
#if (_WIN32) || (_WIN64)
 SOCKET sockfd;
 WSADATA wsaData;
 int iResult;
#else
 int sockfd;
#endif

#if (_WIN32) || (_WIN64)
    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);
        return 1;
    }
#endif

 //basic check of the arguments
 //additional checks can be inserted
 if (argc !=2) {
  perror("Usage: TCPClient <IP address of the server>"); 
  exit(1);
 }
 //Create a socket for the client
 //If sockfd<0 there was an error in the creation of the socket
 if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) <0) {
  perror("Problem in creating the socket");
  exit(2);
 }
 //Creation of the socket
 memset(&servaddr, 0, sizeof(servaddr));
 servaddr.sin_family = AF_INET;
 servaddr.sin_addr.s_addr= inet_addr(argv[1]);
 servaddr.sin_port =  htons(SERV_PORT); //convert to big-endian order
 //Connection of the client to the socket 
 if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr))<0) {
  perror("Problem in connecting to the server");
  exit(3);
 }
 while (fgets(sendline, MAXLINE, stdin) != NULL) {
  send(sockfd, sendline, strlen(sendline), 0);
  if (recv(sockfd, recvline, MAXLINE,0) == 0){
   //error: server terminated prematurely
   perror("The server terminated prematurely"); 
   exit(4);
  }
  printf("%s", "String received from the server: ");
  fputs(recvline, stdout);
 }

#if (_WIN32) || (_WIN64)
 WSACleanup();
#endif

 exit(0);
}

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

Thursday, 14 May 2015

DSP Tech Brief : Neat and tidy FFT functions

Enjoy :


// fft.c

#include <math.h>


void fft_init (double *pFFTCoeffs,
    int FFTSize)

{
    int i;

                // Generate 3/4 Sine look-up-table
    for (i = 0; i < ((3 * FFTSize) >> 2); i++) {
        *pFFTCoeffs++ = sin ((((double)i) * 6.283185307179586476925286766559) / ((double)FFTSize));
    }

}   // End of fft_init()

void rfft (double RealData[],
    double ImagData[],
    const double *pFFTCoeffs,
    int FFTSize,
    int Log2Size)

{
    const double   *pFFTSineCoeffs;
    const double   *pFFTCosineCoeffs;
    int            i, j, k, Stride, BflyCounter, g, h, HalfFFTSize;
    int            Angle, AngleInc;    // Angle step thro sin & cos tables
    double         RealTemp, ImagTemp, Cos, Sin;

    pFFTSineCoeffs = pFFTCoeffs;
    pFFTCosineCoeffs = pFFTCoeffs + (FFTSize >> 2);

    HalfFFTSize = FFTSize >> 1;

    Stride = j = HalfFFTSize;
    Angle = h = 0;

                        // First stage
    for (BflyCounter = 0; BflyCounter < Stride; BflyCounter++) {
        RealTemp = RealData[h] - RealData[j];
        RealData[h] = RealData[h] + RealData[j];
        ImagData[h] = 0.0;      // Clear imaginary part
        RealData[j] = pFFTCosineCoeffs[Angle] * RealTemp;
        ImagData[j] = -pFFTSineCoeffs[Angle] * RealTemp;
        Angle++;
        h++;
        j++;
    }

    AngleInc = 2;

    for (i = 1; i < (Log2Size - 1); i++) {  // Middle stages
        k = Stride;
        Stride >>= 1;
        Angle = 0;
        for (BflyCounter = 0; BflyCounter < Stride; BflyCounter++) {
            Cos = pFFTCosineCoeffs[Angle];
            Sin = pFFTSineCoeffs[Angle];
            Angle += AngleInc;

            h = BflyCounter;
            j = h + Stride;

            for (g = k; g <= FFTSize; g += k, h += k, j += k) {
                RealTemp = RealData[h] - RealData[j];
                ImagTemp = ImagData[h] - ImagData[j];
                RealData[h] = RealData[h] + RealData[j];
                ImagData[h] = ImagData[h] + ImagData[j];
                RealData[j] = Cos * RealTemp + Sin * ImagTemp;
                ImagData[j] = Cos * ImagTemp - Sin * RealTemp;
            }
        }
        AngleInc <<= 1;
    }

                        // Final stage
    for (h = 0, j = 1; h < FFTSize; h += 2, j += 2) {
        RealTemp = RealData[h] - RealData[j];
        ImagTemp = ImagData[h] - ImagData[j];
        RealData[h] = RealData[h] + RealData[j];
        ImagData[h] = ImagData[h] + ImagData[j];
        RealData[j] = RealTemp;     /* Cos = 1, sin = 0 */
        ImagData[j] = ImagTemp;
    }

                        // Reorder scrambled data
    for (j = 0, i = 0; j < FFTSize; j++) {
        if (j < i) {
            RealTemp = RealData[i]; RealData[i] = RealData[j]; RealData[j] = RealTemp;
            ImagTemp = ImagData[i]; ImagData[i] = ImagData[j]; ImagData[j] = ImagTemp;
        }

        k = HalfFFTSize;
        while ((k <= i) && (k >= 1)) {
            i -= k;
            k >>= 1;
        }
        i += k;
    }

}   // End of rfft() 



void cfft (double RealData[],
    double ImagData[],
    const double *pFFTCoeffs,
    int FFTSize,
    int Log2Size)

{
    int          i, j, k, Stride, BflyCounter, g, h;
    int          Angle, AngleInc;    // Angle step thro sin & cos tables
    double       ImagTemp, RealTemp, Cos, Sin;
    const double *pFFTSineCoeffs = pFFTCoeffs;
    const double *pFFTCosineCoeffs = pFFTCoeffs + (FFTSize >> 2);

    for (i = 0, Stride = FFTSize, AngleInc = 1; i < Log2Size; i++) {
        k = Stride;
        Stride >>= 1;
        Angle = 0;
        for (BflyCounter = 0; BflyCounter < Stride; BflyCounter++) {
            Cos = pFFTCosineCoeffs[Angle];
            Sin = pFFTSineCoeffs[Angle];
            Angle += AngleInc;

            h = BflyCounter;
            j = h + Stride;

            for (g = k; g <= FFTSize; g += k, h += k, j += k) {
                RealTemp = RealData[h] - RealData[j];
                ImagTemp = ImagData[h] - ImagData[j];
                RealData[h] = RealData[h] + RealData[j];
                ImagData[h] = ImagData[h] + ImagData[j];
                RealData[j] = Cos * RealTemp + Sin * ImagTemp;
                ImagData[j] = Cos * ImagTemp - Sin * RealTemp;
            }
        }
        AngleInc <<= 1;
    }
    
                        // Reorder scrambled data
    for (j = 0, i = 0; j < FFTSize; j++) {
        if (j < i) {
            RealTemp = RealData[i]; RealData[i] = RealData[j]; RealData[j] = RealTemp;
            ImagTemp = ImagData[i]; ImagData[i] = ImagData[j]; ImagData[j] = ImagTemp;
        }

        k = FFTSize >> 1;
        while ((k <= i) && (k >= 1)) {
            i -= k;
            k >>= 1;
        }
        i += k;
    }
}   // End of cfft()

These functions, and more, are available in the SigLib DSP Library.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

Saturday, 28 February 2015

DSP Tech Brief : Debugging DSP Algorithms And Real-time Applications

I am often asked how I debug real-time and DSP functions. Here is a summary of what works for me.

While there are solutions for displaying signal data graphically, which we will discuss later, I often find that I need to log precise values so that I can check for single bit errors etc. To this end, printf is a pretty good solution but I find that a better one is to use fprintf to log the data to files, this way the tests can be automated and it is easy to use file comparison tools, like KDiff3, or python to check for differences between successive runs.

At a top level I’ll always use a
#define DEBUG_ON 1 // Set to 1 to enable debugging
and then use conditional compilation.

Next, I will use fprintf to send the results to a log file, called debug.log.
So the first thing we need to do is clear any existing open debug.log file, as follows :

int clearDebugfprintf (void)

{
    FILE *fp_LogFile;
    fp_LogFile = fopen( "debug.log", "w");
    if (fp_LogFile == NULL)
    {
        return (1);                  // Return error
    }
    fclose (fp_LogFile);

    return (0);                      // Return no error
}

For the actual logging I will typically use the following two functions or variations of them :

This function works in the same way as printf :

int debugfprintf (const char *ArgumentType, ...)

{
    va_list p_ArgumentList;

   FILE *fp_LogFile;
    fp_LogFile = fopen ("debug.log", "a");
    if (fp_LogFile == NULL)
    {
        return (1);
    }

    va_start (p_ArgumentList, ArgumentType);
    vfprintf (fp_LogFile, ArgumentType, p_ArgumentList);
    va_end (p_ArgumentList);
    fclose (fp_LogFile);

    return (0);
}


This function works in the same way as vprintf :

int debugvfprintf (const char *format,
    va_list ap)

{
    FILE *fp_LogFile;
    fp_LogFile = fopen( "debug.log", "a");
    if (fp_LogFile == NULL)
    {
        return (1);
    }

    vfprintf (fp_LogFile, format, ap);

    fclose (fp_LogFile);

    return (0);
}

One of the most common problems with these functions is how to include them in real-time applications without breaking the response time requirements. JTAG is horrendously slow for this requirement but on my favourite XMOS microcontrollers the debug data can be sent back to the development workstation using a parallel data path, called xSCOPE, in real-time - https://www.xmos.com/products/tools/xscope. xSCOPE uses the xCONNECT data path to route the data with very little overhead on the application being debugged.

To display data graphically I will typically use Gnuplot, as I documented in this blog post http://realgonegeek.blogspot.co.uk/2014/01/interfacing-cc-to-gnuplot.html, or the XMOS xSCOPE graphical display in xTIMEcomposer.

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix

Saturday, 17 January 2015

DSP Tech Brief : How To Perform An FFT On A Real-Time Data Stream

In this example we are going to use the SigLib DSP Library functions to perform a Fast Fourier Transform on the Analog inputs on our laptop or desktop computer.

The analog I/O functionality is based on the code described in this blog entry :
How To Connect Digital Signal Processing Functions To An ADC And A DAC

We can use the free version of SigLib (no source code) from here : http://www.numerix-dsp.com/free/.
Before proceeding any further, please ensure :

  That SigLib is installed and configured correctly for your compiler. Full details are included in the SigLib User's Guide which is provided with the library and also available here : http://www.numerix-dsp.com/docs/.
  That GnuPlot and Gnuplot/C are both installed and working correctly : http://realgonegeek.blogspot.co.uk/search?q=gnuplot.

The code uses a double buffering technique so that the analog input code writes the data into a separate array to the one being processed with the FFT functions.

Here is the source code for the top level function (pa_fft.c) :

#include <stdio.h>
#include <math.h>
#include "analog_io.h"
#include <siglib.h>                                 /* SigLib DSP library */
#include <gnuplot_c.h>                              /* Gnuplot/C */

#if _MSC_VER                                        // If compiler not MSVC then include kbhit.h
#include <conio.h>
#else
#include "kbhit.h"
#endif

#define SAMPLE_RATE     44100

#define FFT_LENGTH      ((SLArrayIndex_t)1024)
#define LOG2_FFT_LENGTH ((SLArrayIndex_t)10)
#define HALF_FFT_LENGTH (FFT_LENGTH >> 1)
#define WINDOW_SIZE     FFT_LENGTH

/* Declare global variables and arrays */
SLData_t    *pRealData, *pImagData, *pWindowCoeffs, *pResults, *pFFTCoeffs;


volatile int Input_SamplesCount;
volatile int Input_Data_Valid_Flag;
volatile int Input_Data_Buffer;

SLData_t  *pInputData, *pProcessData;
SLData_t  Data1 [FFT_LENGTH], Data2 [FFT_LENGTH];

h_GPC_Plot  *h2DPlot;                           /* Plot object */

void analog_isr (void)
{
                            // Process channel 0 - Store data for FFT and output zero
  *(pInputData+Input_SamplesCount) = (double)adc_in0;
  dac_out0 = 0;
                            // Process channel 1 - just output zero
  dac_out1 = 0;

  Input_SamplesCount++;
  if (Input_SamplesCount == FFT_LENGTH)           // If we have filled the buffer, mark data as valid and swap buffers
  {
    Input_Data_Valid_Flag = 1;                    // FFT Data is now valid
    Input_SamplesCount = 0;                       // Reset input sampels count

    if (Input_Data_Buffer == 1)
    {
      pInputData = Data2;                         /* Input to array 1  */
      pProcessData = Data1;                       /* Perform FFT on array 2 */
      Input_Data_Buffer = 2;
    }
    else              // (Input_Data_Buffer == 2)
    {
      pInputData = Data1;                         /* Input to array 1  */
      pProcessData = Data2;                       /* Perform FFT on array 2 */
      Input_Data_Buffer = 1;
    }
  }
}

void process_FFT (void)

{

  SDA_Copy (pProcessData, pRealData, FFT_LENGTH); // Copy data for processing

  Input_Data_Valid_Flag = 0;                      // FFT Data has been copied for processing

                                                  /* Apply window to data */
  SDA_Window (pRealData,                          /* Pointer to source array */
              pRealData,                          /* Pointer to destination array */
              pWindowCoeffs,                      /* Pointer to window oefficients */
              WINDOW_SIZE);                       /* Window length */

                                                  /* Perform real FFT */
  SDA_Rfft (pRealData,                            /* Pointer to real array */
            pImagData,                            /* Pointer to imaginary array */
            pFFTCoeffs,                           /* Pointer to FFT coefficients */
            SIGLIB_NULL_ARRAY_INDEX_PTR,          /* Pointer to bit reverse address table - NOT USED */
            FFT_LENGTH,                           /* FFT length */
            LOG2_FFT_LENGTH);                     /* log2 FFT length */

  SDA_LogMagnitude (pRealData,                    /* Pointer to real source array */
                    pImagData,                    /* Pointer to imaginary source array */
                    pResults,                     /* Pointer to log magnitude destination array */
                    HALF_FFT_LENGTH);             /* Data dataset length */

  SDA_Offset (pResults, -138.0, pResults, HALF_FFT_LENGTH); // Offset graph results for 0 dB

  gpc_plot_2d (h2DPlot,                           /* Graph handle */
               pResults,                          /* Dataset */
               HALF_FFT_LENGTH,                   /* Dataset length */
               "FFT of ADC Input Data",           /* Dataset title */
               SIGLIB_ZERO,                       /* Minimum X value */
               (double)(HALF_FFT_LENGTH - 1),     /* Maximum X value */
               "lines",                           /* Graph type */
               "blue",                            /* Colour */
               GPC_NEW);                          /* New graph */

}

int main(void)
{
  int Error;

  Input_SamplesCount = 0;                         // Initialize flags
  Input_Data_Valid_Flag = 0;
  Input_Data_Buffer = 0;

  pInputData = Data1;                             /* Input to array 1  */
  pProcessData = Data2;                           /* Perform FFT on array 2 */

                                                  /* Allocate memory */
  pRealData = SUF_VectorArrayAllocate (FFT_LENGTH);
  pImagData = SUF_VectorArrayAllocate (FFT_LENGTH);
  pFFTCoeffs = SUF_FftCoefficientAllocate (FFT_LENGTH);
  pResults = SUF_VectorArrayAllocate (FFT_LENGTH);        /* RMS result array */
  pWindowCoeffs = SUF_VectorArrayAllocate (WINDOW_SIZE);  /* Window array */

  h2DPlot =                                       /* Initialize plot */
      gpc_init_2d ("Fast Fourier Transform",      /* Plot title */
                   "Frequency",                   /* X-Axis label */
                   "Magnitude",                   /* Y-Axis label */
                   120.0,                         /* Scaling mode */
                   GPC_NEGATIVE,                  /* Sign mode */
                   GPC_MULTIPLOT,                 /* Multiplot / fast plot mode */
                   GPC_KEY_DISABLE);               /* Legend / key mode */
  if (h2DPlot == NULL)
  {
      printf ("\nPlot creation failure.\n");
      exit (1);
  }

  SIF_Window (pWindowCoeffs,                      /* Pointer to window oefficient */
              SIGLIB_HANNING,                     /* Window type */
              SIGLIB_ZERO,                        /* Window coefficient */
              FFT_LENGTH);                        /* Window length */

                                                  /* Initialise FFT */
  SIF_Fft (pFFTCoeffs,                            /* Pointer to FFT coefficients */
           SIGLIB_NULL_ARRAY_INDEX_PTR,           /* Pointer to bit reverse address table - NOT USED */
           FFT_LENGTH);                           /* FFT length */

  Error = analog_open (SAMPLE_RATE, analog_isr);  // Open the analog interface
  if (Error == -1)
    return 1;

  printf("Hit ENTER to stop program.\n");
  while (!kbhit())                                // Wait until key hit
  {
    if (Input_Data_Valid_Flag == 1)               // If data is valid process the FFT
    {
      process_FFT ();
    }
  }

  Error = analog_close ();                        // Close the analog interface
  if (Error == -1)
    return 1;

  gpc_close (h2DPlot);

  SUF_MemoryFree (pRealData);                     /* Free memory */
  SUF_MemoryFree (pImagData);
  SUF_MemoryFree (pResults);
  SUF_MemoryFree (pWindowCoeffs);
  SUF_MemoryFree (pFFTCoeffs);

  return 0;
}

Notes :
  The global variables are decalared as volatile. This prevents the compiler optimizing away the code, which often happens when variables are used in interrupt service routines because the compiler has no way of knowing how the ISR is going to be called.
  GCC and some other compilers do not implement the kbhit () function so I have included the code from here : http://cboard.cprogramming.com/linux-programming/51531-faq-cached-input-mygetch.html#post357655.

Testing

Under Microsoft Visual Studio the code can be built using the following command (assuming you have already got a working PortAudio installation) :
cl pa_fft.c analog_io.c -W4 -DPA_USE_ASIO=1 -D "SIGLIB_STATIC_LIB=1" -D "_CRT_SECURE_NO_WARNINGS=1" portaudio_x86.lib siglib.lib gnuplot_c.lib nhl.lib

My laptop only supports a single microphone input to the ADC so to test the code I used an XMOS Multi-Function Audio (MFA) platform, shown in this image :



The XMOS xCORE processor on this board is more than capable of handling the signal processing code shown in this blog but for the purposes of the demonstration I am running the DSP code on the host.

If you run the code yourself you will see the the FFT output plotted in Gnuplot and should look something like this - if you apply a noisy sinewave to the input ;-) :


Stay tuned for a future blog post where I will implement this code and the IIR filter code on an XMOS xCORE :-)

If you have found this solution useful then please do hit the Google (+1) button so that others may be able to find it as well.

Numerix-DSP Libraries : http://www.numerix-dsp.com/eval/

Copyright © 2015 Delta Numerix