THE BASIC DELPHI SIGNAL

PROCESSING PACKAGE

 

About the package

       Signal processing is used in many different areas of science and sound processing is just one of them. The package was designed to offer an easy and affordable start in to the world of signal processing. By providing three key components in combination with the ease of use and the power of Delphi, makes it a strong argument towards very expensive DSP suites. The aim of this package however is not to compete with the big ones, but to give individual users access to the most needed features when dealing with signals, without having to empty their wallet for things that they will not know how to use or need for next several years or never. Most of the standard DSP operations like: FFT, run-time filter design and filtering, decimation, interpolation, convolution can be done easily and are available within these package.

 

Speed

 

The package uses the Intel signal processing suite and the user is free to use other functions from that suite in combination with the components presented here. The package uses 6 DLL libraries which have been down sized to about 150 kb from more than 10 MB of the original size of the Intel's suite. The Intel itself had nothing to do with the creation of this package and the name Intel here is used ONLY as a reference to their library. Many times the programmers desire the native Delphi components. This has some good and some bad sides especially when it comes to highly optimized code.

    The distribution of the applications made with this package therefore requires only 150 kb in DLL files. If the user would want to use the original 10 MB file is that not a problem. The Intel libraries are optimized for different processors and are among the fastest in the PC world.

 

The components included with the package

 

TFFTAnalysis - calculates frequency spectrum and offers several different options.

TFIRAnalysis - filters a signal continuously or in batch mode.

TSignalGenerator  - generates 8 different signal types.

TFileStoring - provides an easy way to load signals from files.

 

Plus multichannel versions of each of the components: TFFTList, TFIRList, TSignalList, TFileList.

 

Engineering quality

 

The main question is always the correctness of the calculation. How reliable are the results at all. If the calculation was not performed as it should be then the interpretation of the results is pointless. Here comes in the shareware application FFT Properties. The application was build with the components in question. The properties of the components and the edit boxes of the FFT Properties coincide almost completely. Therefore, it is always possible to check the calculation procedure towards a working application and know in advance what the results should be. The main method for assuring the proper calculation procedure is the use of a known signal at the input (signal generator) and knowing in advance what the output should be (FFT). This gives the user fairly good confidence in the results of his own work.

 

The price and the technical support

 

The price of the package  is 29$. (Twenty nine American dollars). The technical support is available for free from the date of purchase until it works but no longer than one week. Trail versions are available and are included with FFT Properties 2.5. The registration is performed on-line by RegSoft.com.

 

The price of the application FFT Properties: 39$.

The price of the Basic Delphi signal processing package: 29$.

The price of the complete suite (FFT Properties + package): 49$.

 

Reference of the package components:

 

Things in common

Each of the components require as input a valid TMeasuringPoint component (MPoint). The reason why a special component is preferred will be come more understandable later. That MPoint component holds the actual data (the signal) together with the basic information about that signal. The basic information includes the number of samples (MPoint.Length (integer)) the time in which those samples were acquired (MPoint.SampleTime (single), the information if that channel is Active (MPoint.Active (boolean)) and  the information about the sensor units (string). Each of the analysis components have a boolean property Active. If the property is True then the calls to recalculate data will be processed, and if False the calls to the recalculate data will be suppressed. The calls will be also suppressed if the MPoint assigned is not active. This is a convenient way to turn separate channels on and off  and to make analysis active or not. Since the basic information about the signal is contained in one object (Mpoint), all the analysis objects that work on the data contained in MPoint, have all the needed information about the signal available simply by changing the properties of one MPoint object.

    FIRAnalysis and SignalGenerator overwrite the data in MPoint with the new filtered signal and the FFTAnalysis stores the frequency spectrum in the MPoint and retains the original signal data in the MPoint. Mpoint holds two data objects. One contains the signal itself and the other is prepared for the frequency spectrum. It may seem strange why is the frequency spectrum included with signal, but it is very often that the frequency spectrum is desired right next to the time signal (almost like the basic data). The recalculation of the components is triggered by a simple call to AnalyzeData or ProcessData. The changing of properties does not trigger any recalculation except maybe the recalculation of constraints imposed on values of the other properties of the same component. Recalculation is performed only by a direct call to AnalyzeData.

TSignalGeneration Component

Reference

 

The Signal generation component allows the generation of eight different signal types. The signal types are: TSignalType = (stSine, stImpuls, stTriangle, stSquare, stChirp, stMultiply, stDelta, stRandomNoise, stGaussNoise). The signal type is defined with the property: SignalType. By changing the properties of the component no recalculation is performed until a direct call to ProcessData. With one call to the ProcessData up to 3 sine's can be created.

 

Properties

 

property Active: boolean;

 

If true the ProcessData method will recalculate data.

 

property ParameterError: boolean;

 

A read only property, if the ProccessData method does not succeed because of a numerical error or an empty  invalid settings in MPoint object. (MPoint = nil or MPoint.Length = 0 etc..).


property RealTime: boolean;

 

If the property is true, than each consecutive call to process data will return in MPoint.Data array the values that are a continuation (in time) of the previous block of data generated by the component. The period in number of points is 2 000 000. After 2 000 000 values have been calculated the counter is reset to zero. Future versions will include infinite period functions for sine's and triangles.


property AdjustPhase: boolean;

 

if true adds a delta function of magnitude 0.001 to the signal. This insures that the phase will not be random but zero everywhere where the signal does not have any frequencies.


property FDV: single;

 

The number that divides the frequencies set by SF1, SF2, SF3 to obtain valid non-integer number of periods per time interval.


property SF1,2,3: single;

 

Frequency in Hz of the signal 1,2,3.


property SP1,2,3;

 

Phase in degrees of the signal 1,2,3


property SA1: single read FSA1 write SetSA1;

 

Amplitude of the signal 1,2,3.


property DC1: single read FDC1 write SetDC1;

 

DC component of signal 1,2,3. In case of  stRandomNoise the DC1 represents the lower value and the DC2 the upper value of the random noise. In case of the   stGaussNoise the DC1 is the average and DC2 is the standard deviation.

 
property Harmonic: boolean;


The signals 2 and 3 will be higher harmonics of the frequency 1. The SF2 and SF3 are automatically calculated. For the higher harmonics to become visible, the SA2 and SA3 must different from zero.


property SignalType: TSignalType;

 

TSignalType = (stSine, stImpuls, stTriangle, stSquare, stChirp, stMultiply, stDelta, stRandomNoise,

stGaussNoise). Sets the signal type.

 

Sinus: The signal generated is sine. You can have multiply sine signals (maximum of three). Signal 1,2,3 groups set the parameters

Triangle: The signal generated is triangle. Signal 1 group sets the frequency amplitude, DC and phase. You can not have multiply triangle signals.

Square: Creates a rectangular signal. Signal 1 group sets the parameters. You can not have multiply square signals.

Chirp: Also called transient. The Signal 1 group defines the starting frequency and all the other parameters. The Signal 2 frequency defines only the ending frequency. You can not have multiply chirp signals. (The frequency spectrum of a chirp signal is usually a very wide peak or a series of wide peaks, If there is more then one chirp present in the signal. The amplitude of the actual frequencies is greatly reduced and we can not talk about amplitude accuracy.)

Impulse: This is the well-known function: sinc(x) = sin (x)/x, one sided. The Signal 1,2,3 groups define the signal. You can have multiply impulse signals combined. (This function is used for FIR filtering.)

Multiply1&2: The Signal 1 and Signal 2 are combined by multiplication. Signal 1,2 groups set the parameters. (Non-linear coupling).

Delta function: Creates a delta function. The amplitude of 1 defined in SA1 is multiplied with the NumberOfSamples/2.

Gaussian noise: The noise has two parameters: Average: defined as DC1 /10 and Standard deviation defined as DC2 /10.

Random noise: The noise has two parameters: Upper value: defined as DC1 /10 and Lower value defined as DC2 /10.

 

property MPoint: TMeasuringPoint;

 

The pointer to MPoint object that actually holds the MPoint.Length number of single precision floating point values in the MPoint.Data array.

 

Methods

 

procedure ProcessData;

 

Triggers recalculation.


TFileStoring component

 

Reference

The FileStoring  allows quick loading of three different file types. Those file types are the same as those in FFT Properties. It support MPoint and makes it really easy to load data from a file.

 

Properties

 

property Active: boolean;

 

If true the ProcessRecord method will recalculate data.


property FileName: string;

 

Defines the file name, that should be loaded. Before calling the method LoadFile, the FileName property must be set.


property MPoint: TMeasuringPoint;

 

The pointer to MPoint object that actually holds the MPoint.Length number of single precision floating point values in the MPoint.Data array that were read from the file.

 

property NumberOfRecords: integer;

 

Read only property. Returns the number of records in the currently loaded file.


property InputError: boolean;

 

Read only property. It Is true if and error has occured while reading or processing the file.

 

Methods


procedure ProcessRecord(RecordNumber: integer);

 

After the file has been loaded, the call to ProcessRecord loads the MPoint with the data from the record with the number: RecordNumber. The RecordNumber ranges from 1 to NumberOfRecords. The procedure will do nothing if the LoadFile was not called first.


procedure LoadFile;

 

Reads the file from the disk in to memory. The file does not need to be unloaded. When the next file is loaded the memory will be freed and reallocated. When the object is destroyed the allocated memory is freed.

 

TFIRAnalysis Component

Reference

The FIR Analysis allows the filtering of a continuos time series. It features multiband and multirate capabilities.

 

Maximum number of taps: 5000

Maximum reachable stopband attenuation: 130 dB.

Preset filter types: Low pass, High pass, Band pass. Band stop.

Calculation precision: single (4 bytes).

Design method: Kaiser window.

The visualization and measurement of the filter response characteristics is provided by FFT Properties.

 

The user can access FIRTaps.Data array directly to change the desired impulse response. Normally the impulse response can be designed by the component copied to a new location, further manipulated and then copied back to the FIRTaps.Data. To initialize the calculation of the changed impulse response the Init method must be called. If the taps are not changed than it is sufficient to call the Initalize method and the filter is ready to process data by calling either ProcessData method or ProcessSample method.

    When designing the filter it is advisable that the filter is first visualized in the FFT Properties to see, how the filter looks and if the settings work as anticipated. When setting the properties of the FIR component the user should follow the correct order of steps. First, the center frequency must be defined. Second comes the bandwidth (of the passband). And then all the rest. There are constraints imposed on the values of the fields. The fields are automatically updated if the settings do not reflect a valid filter. To prevent automatic update while changing the properties the AutoCalculate should be set to false. But before the call to ProcessData the Initialize method should be called. To experience the constraints imposed by the filter design it is advisable to first try to design some filters visually within FFT Properties.

   It is a well known fact that filter design is sampling frequency independent. The FIRAnalysis component allows the user to enter the filter requirements in Hz. This makes it easier to design a filter for inexperienced users. The frequency independence can be stated as: Fc/Fs = konst, where Fc is center frequency and Fs is sampling frequency. If the filter transition bandwidth is expressed as: TransitionBW1/FS = konst, the new transition bandwidth (in Hz) can be calculated as TransitionBW = FS*konst. The impulse response (filter) for the same konst value (actually omega) has  the same length and the impulse response is numerically the same.

 

Properties

 

property FirTaps: TTimeSignal;

 

Read only property. FirTaps.Data contains the filter impulse response. The length of the Data arrays is defined by FIRTaps.Length. There are two ways to read and write data from the TTimeSignal object. The object has a default array property and it is possible to write: FIRTaps[i] := MyValue; The size of the array is set by the FIRAnalysis component. Lower and upper bound checking is performed if default array property is used. This ensures that only valid data is returned and that non-allocated memory can not be compromised if the user would write to the Data array. On the other hand. this is much slower then if the array is referenced directly: FIRTaps.Data^[i]. The pointer dereference operator (^) can be omitted in Delphi 4.0. The FIRTaps allow the design of a user definable multiband filter (equalizer).

 

property MPoint: TMeasuringPoint;

 

The pointer to MPoint object that actually holds the MPoint.Length number of single precision floating point values in the MPoint.Data array. After the call the method ProcessData the MPoint is resized in case of interpolation or decimation and contains the new filtered signal. The old data is overwritten.


property FilterType: TFilterType;

 

TFilterType = (ftLowPass, ftHighPass, ftBandPass, ftBandStop);

 

Defines the filter type.


property CenterFrequency: integer;

 

The center frequency of the pass band. It is limited between 0 and total signal bandwidth in Hz.

 

property Bandwidth: integer;

 

The width of the passband in Hz. It is limited to (CenterFrequency-Bandwidth/2) > 0 and (CenterFrequency + Bandwidth/2) < total signal bandwidth.


property TransitionBW1: integer

 

The width of the transition bandwidth 1. It defines the transition bandwidth for lowpass, bandpass and bandstop filters.

 

property TransitionBW2: integer;

 

The width of the transition bandwidth 2. It defines the transition bandwidth for the highpass, bandpass and bandstop filters.

 

property StopBandAtt: single;

 

Defines the attenuation in dB of the stopband.

 

property FilterLength: integer;

 

Read only. The length of the FIRTaps.Data array and equal to FIRTaps.Length. Defines the number of taps.


property Ripple: single;

 

It is in direct connection with the StopBandAtt property. It defines the ripple of the passband. Only one property can be set and the other is adjusted to match the first.

 

property UpSample: integer;

 

Defines the interpolation factor. The new signal will contain UpSample times more points.

The filter uses polyphase structure to calculate the multirate filter.


property UpDelay: integer;

 

Defines the starting Phase delay in number of samples.

 

property DownSample: integer;

 

Defines the decimation factor. The new signal will contain UpSample times less points.

The filter uses polyphase structure to calculate the multirate filter. In combination with UpSample a non-integer sampling frequency change can be achieved. (NewSamplingFrequency := OldSamplingFrequency*UpSample/DownSample).

 

property DownDelay: integer;

 

Defines the starting Phase delay in number of samples.


property AutoCalculate: boolean;

 

If true the filter will be recalculated each time a property is changed.

 

Methods

 

procedure ApplyChanges;

 

Recalculates the FIR impulse response stored in the FIRTaps.Data array.


procedure Init;

 

Prepares the FIR for calculation based on the current values in the FIRTaps. This initialization must be performed before calling the ProcessData either by this method or by the Initialize method.


procedure ReInitialize;

 

The method will ApplyChanges and Init the filter if the AutoCalculation is true.

 

procedure Initialize;

 

The method will ApplyChanges and Init the filter


procedure AnalyzeData; override;

 

Same as ProcessData.


procedure ProcessData;

 

Performs the actual filtering of the signal stored in MPoint.Data. The result replaces the original values.


function ProcessSample(Sample: single): single;

 

Performs the actual filtering of the signal that is passed sample by sample as an argument to the procedure. The result is returned as a function result sample by sample.


TFFTAnalysis Component

 

Reference

The FFT Analysis allows calculation of the FFT in just few lines of code. The technical data is as follows:

 

Numerical signal to noise: 150dB.

Type of algorithm:   common RADIX2.

Calculation procedure:   Using only half the size of the original signal for calculation of the spectrum. (standard RealFFT)

Output:  Amplitude and Phase spectrum.

 

Speed:

 

33 ms for 4096 points on P133.

15 ms for 2048 points.on P133

6.8 ms for 1024 points on P133.

3 ms for 512 points. on P133

 

(Including scaling, windowing and amplitude and phase spectrum calculation}

Features

Properties

The component has the following properties:

property Window: TTimeWindow;

TTimeWindow = (wtRectangular, wtHanning, wtHamming, wtFlatTop, wtBartlet,wtBlackman, wtExponent);

Defines the type of Window used. If you do not wish to use any windowing set the property to wtRectangular.

property AmpltType: TAmpltType;

TAmpltType = (atNormal, atRMS, atPower);

Defines the Spectrum type. This can be on of the following: Normal spectrum, RMS spectrum, Power spectrum.

property LogType: TlogType;

TLogType = (ltAbsolute, ltRelative);

The logarithmic spectrum is calculated in dB. If you wish that the maximum value of the spectrum is zero then set this value to: ltRelative.

property Logarithmic: boolean;

Set the spectrum to be logarithmic in dB.

property LogDBSpan: TLogDBSpan;

Defines the dynamic range of the spectrum.

TLogDBSpan = (ls60, ls90, ls120, ls150)

property DCDump: boolean;

If you wish to remove the DC component of the signal in spectrum automatically set DCDump to true. This does not distort the signal in any way.

property ScaleFactor: integer;

To scale spectrum for unit changes or else.

property Averaging: TAveraging;

TAveraging = (avNone, avInfiniteLinear, avInfiniteExponential);

There are two types of averaging supporting infinite linear and infinite exponential averaging. The exponential decay factor is defined by the property ExponentialDecay;

property ExponentialDecay: cardninal;

Used by infinite exponential averaging.

property MPoint: TSpectrumPoint;

Run time only. For the component to function it requires this property to be filled. TSpectrumPoint is a component that is a part of the package.

property ResultData: TSpectrum;

Run time only. This object holds result data. See the Chapter The Concept for additional information.

Methods

procedure AnalyzeData;

Triggers recalculation of the spectrum. Changing the properties does not trigger recalculation. Calling AnalyzeData is the only way to recalculate. The inverse is not supported through the component because of the additional features of the component. However. The inverse can be calculated by calling the low level function: ComplexFFT or RealFFT.

procedure ComplexFFT(n: integer; Data :array of single; nn: integer);

Input and output has the following format:

{ RealData: Data[1], Data[3] ...

CmpxData: Data[2], Data[4] . }

 

nn: can be 1 for forward FFT or –1 for inverse FFT.

n: is the size of the FFT

procedure RealFFT(n: integer; Data :array of single; nn: integer);

The input is 1 based array with real values.

{RealData: Data[1], Data[2], Data[3],.....

The output is 1 based array with complex values:

{ RealData: Data[1], Data[3] ...

CmpxData: Data[2], Data[4] . }

 

nn can be 1 for forward FFT or –1 for inverse FFT. The RealFFT function is about two times faster then the Complex version if the input data is not complex. data.

n is the size of the FFT.

TMeasuringPoint Component

TMeasuringPoint component is an abstract type. The implementation is performed in the object TSpectrumPoint. It containes the signal being processed and the necessary description of it.

property SampleTime: Single;

One of the two key properties. It sets the Length of the data block in seconds. It is required to set this property before the object can be used.

property Length: Integer;

The second of the two key properties. It sets the Length of the data block in number of values (samples). It is required to set this property before the object can be used.

property TimeSignal: TTimeSignal;

It is used to assign fresh data. Make sure to set TimeLength and Length before filling this object with data.

TSpectrum object

PSpectrum = ^TSpectrum;

TSpectrum = class(TObject)

TDynSingleArray = array [0..MaxPoints] of Single;

PDynSingleArray = ^TDynSingleArray;

TSpectrum is a nonvisual object. It is the basic storage object for storing one frequency spectrum. The properties of the object are mostly read only, since the values are set by the FFTAnalysis component.  It features automatic dynamic allocation of memory and stores data that describe the spectrum itself. It has the following properties:

property Amplt: ^TDynSingleArray;

This property hold the pointer to the array of single that holds the Amplitude spectrum

Phase: ^TDynSingleArray;

This property hold the pointer to the array of single that holds the Phase spectrum

property DataArray: TDataArray;

TDataArray = (taAmplt, taPhase);

The TSpectrum object has a default array property. The user can reference the values simply by: My Value := Spectrum[i],   where Spectrum is a TSpectrum object. The property DataArrays defines which values will be returned: Amplitude or Phase. The use of default array property slows down the read out of the results, but ensures that nor upper nor lower bound of the dynamically allocated array will be crossed with invalid results returned. After the application has been run-in the direct addressing of the array can be used like this: Spectrum.Amplt^[i]. This is  theoretically the fastest way to get data out of the object with the idea of dynamic memory allocation in mind. (The pointer reference can be omitted in Delphi 4.0). 

property BandWidthL: Single;

Lower frequency limit in Hz. It is calculated automatically and it is not meant to be used by setting its value.

BandWidthH: Single;

Upper frequency limit in Hz. It is calculated automatically and it is not meant to be used by setting its value. You can read it and reference it, but do not set the value.

MaxAmplt: Single;

The maximum value of the Amplitude spectrum.

property Length:

It defines the Length of the the spectrum and allocates memory required for storing the data. The TSpectrumPoint sets this value automatically.

TTimeSignal

TTimeSignal = ^TTimeSignal;

TTimeSignal = class(TObject)

 

property Data: ^TDynSingleArray;

Holds the TimeSignal data.

property MaxAmplt: Single;

Holds the Maximum amplitude of the data. It is not calculated automatically. TFFAnalysis sets this values.

property MinAmplt: Single;

Holds the Minimum amplitude of the data. It is not calculated automatically. TFFAnalysis sets this values.

property MinX: single;

Start of the data in seconds. Usually 0 and recalculated when property Length is changed.

property MaxX: single;

End of the data in seconds. Usually the length of the data block in seconds. Recalculated when property Length is changed.

property SamplingFrequency: Single

Recalculated when property Length is changed. Returns the sampling frequency. Read only.

property SampleLength: Single;

One of the two key properties. It sets the Length of the data block in seconds. It is required to set this property before the object can be used. This property is also set by TSpectrumPoint.

property Length: Integer;

One of the two key properties. It sets the Length of the data block in number of values (samples). It is required to set this property before the object can be used. This property is also set by TSpectrumPoint. The Destroy method will clean up the memory allocated.

Multichannel components

The mutlichannel components allow the user to handle the components in blocks. It is many time desirable to change the values of some property of all the components present or to issue a centralized recalculation request. They multi-ch components are:

 

TFileStoringList

TFFTList

TSignalList

TFIRList

 

Each of the four main components: TFFTAnalysis, TFIRAnalysis, TSignalGenerator and TFileStoring, have their multichannel equivalents. To illustrate on an example here is the multichannel version of TFileStoring:

To add a TFileStoring object TFileStoringList we can call:

function Add(Storing: TFileStoring): integer;

The function returns the position where the new object has been inserted.To create a new TFileStoring object and assign an MPoint object to it:

function AddStorage(MPoint: TMeasuringPoint): integer;

The objects within the TFileStoringList will be destroyed when the TFileStoringList is destroyed.

function Count: integer;

Returns the number of TFileStoring objects within the TFileStoringList.

procedure Delete(Index: integer);

Removes the TFileStoring objects from the TFileStoringList. but does not Destroy the object removed.

Default array property enables the user to easy access each of the objects within the container:

AFileList[Channel].ProcessRecord(i);

To issue the same request to all the objects within the AFileList container we can write: AFileList.ProcessRecord(i).

Getting started

We can start by first placing two components on the form: TFFTAnalysis and TSpectrumPoint. Then we assign the SpectrumPoint1 to FFTAnalysis property MPoint. To defined the number of samples and time in which those samples were acquired we set:

 

SpectrumPoint.Length := YourLength; {It defines the number of samples that you wish to analyze}

SpectrumPoint.SampleTime := TimeLength.; {It defines the time in which those samples were acquired}

 

The value: YourLength/TimeLength equals Sampling Frequency.

 

Now the array can be filled with data:

 

for i:= 0 to SpectrumPoint.Length-1 do

SpectrumPoint.TimeSignal.Data^[i] := values....;

To issue the request for recalculation: FFTAnalysis.AnalyzeData;

 

To retrieve the result data can do the following:

 

for i := 0 to FFTAnalysis.ResultData.Length-1 do

begin

yourdata[i] := FFTAnalysis.ResultData.Amplt^[i];

yourdata2[i] := FFTAnalysis.ResultData.Phase^[i];

end;

 

Actual sample code from the Delphi project:

begin

    MyLength := 256;

    SpectrumPoint1.Length := MyLength;

    SpectrumPoint1.SampleTime := 1;

    for i:=0 to SpectrumPoint.Length-1 do

        SpectrumPoint1.TimeSignal.Data^[i] := sin(UpDown1.Position*(i)*2/MyLength*Pi);

    Chart1.AutoRepaint := False;

    {.

    ...... Change any parameters of FFTAnalysis

    .}

    FFTAnalysis1.AnalyzeData;

    Series1.Clear;

    with FFTAnalysis1.ResultData do

    begin

         for i:=0 to Length-1 do Series1.AddXY(BandWidthL+(i)/Length*BandWidthH, Amplt[i],'',clTeeColor);

    end;

   Chart1.AutoRepaint := True;

    Chart1.Repaint;

end;

 

 

Chart1 is TeeChart by David Berneda included with Delphi 3.0 and 4.0.