Ham Cockpit   

    Show / Hide Table of Contents

    DSP Functions

    The DSP Fun library that comes with Ham Cockpit includes a number of signal processing classes that the plugin authors may use in their projects. The library is published with source code GitHub, the class reference is here. This article provides some code examples. For more examples, please see Demodulator Plugins.

    AudioClientErrors

    This class is useful when working with CSCore, a popular audio I/O library. It provides the error messages not defined in the library and should be used like this:

    private void RethrowException(Exception e)
    {
      if (e is CoreAudioAPIException && e.Message.Contains("Unknown HRESULT"))
        message = AudioClientErrors.Message((e as CoreAudioAPIException).ErrorCode);
      else
        message = e.Message;
    
      throw new Exception(message);
    }
    

    ChannelSelector

    is another class that works with the CSCore library. it is used to extract one of the channels from a multi-channel audio data stream. Usage:

    CSCore.ISampleSource source = new ChannelSelector(source, ChannelMask.SpeakerFrontLeft);
    ...
    source.Read(buffer, offset, count);
    

    When the Read method is called on the ChannelSelector object, it reads multi-channel data from the source that was passed to the constructor and returns only the samples that belong to the specified channel (the SpeakerFrontLeft channel in the example above). Since both source and ChannelSelector implement the CSCore.ISampleSource interface, it is easy to chain multiple signal processors like this.

    RealFft and ComplexFft

    are wrappers around the FFT functions in the Intel IPP library, one of the best speed-optimized DSP libraries. These classes hide the complexity of invoking the IPP functions behind a simple interface:

    //create FFT object
    const int FFT_SIZE = 65536;
    ComplexFft fft = new ComplexFft(FFT_SIZE);
    
    //put time-domain data multiplied by the window function in fft.TimeData
    for (int i = 0; i < FFT_SIZE; i++)
      fft.TimeData[i] = data[i] * window[i];
    
    //compute forward FFT and put results in fft.FreqData
    fft.ComputeForward();
    
    //compute power spectrum from FreqData
    float[] pwr = fft.PowerSpectrum();
    
    

    An example of RealFft is available in the Demodulator Plugins.

    ComplexFirFilter and RealFirFilter

    is another pair of wrappers around the IPP functions. Sample code is available in Demodulator Plugins.

    Dsp.ApproximateRatio()

    finds a rational (L / M) approximation of a floating point value with smallest possible L and M values. Useful for setting up sampling rate converters, such as IppResampler. Small L and M reduce the complexity of the resampler. Example:

    var InputRate = 44100;
    var DesiredRate = 48000;
    //allow the output rate to be within +/-1% from the desired value
    var RateTolerance = 0.01f;
    
    var (L, M) = Dsp.ApproximateRatio(DesiredRate / InputRate, RateTolerance);
    var OutputRate = (InputRate * L) / M;
    
    int filterLength = (60 * M) / L;
    //when resampling, allow the last 3% of the bandwidth 
    //to be contaminated with mirror images
    var usefulBandwidth = 0.97f;
    
    var resampler = new IppResampler(M, L, filterLength, usefulBandwidth, 10);
    

    IppResampler

    An instance of IppResampler may be created as shown in the ApproximateRatio section. If the data are complex and/or multi-channel, a separate resampler is used for each component. In the example below, two instances are used to resample I/Q data. The offset and stride parameters of the Process method allow resampling multi-component data stored in a floating point array:

      int resampledCount = resamplerI.Process(inputData, 0, stride, count);
      resamplerQ.Process(inputData, 1, stride, count);
    

    Resampled data are stored in the IppResampler.OutData property.

    MultipassAverage

    is used to apply multiple passes of the moving average filter to real data, one sample at a time.

    var stageDelay = 10; //in samples
    var boxLength = 2 * stageDelay + 1;
    var numberOfStages = 4;
    var totalFilterDelay = stageDelay * numberOfStages;
    
    var avg = new MultipassAverage(boxLength, numberOfStages);
    ...
    float outputSample = avg.Process(inputSample);
    

    OmniRigClient

    The OmniRigClient class talks to the OmniRig engine and uses it to control the radio via its CAT interface. Only the radio interfacing plugins need to use this class directly, all other plugins should talk to the radio plugin to read and set the radio parameters as demonstrated in the Frequency Display Demo plugin.

    Create an instance of the OmniRigClient class:

    private readonly OmniRigClient Cat = new OmniRigClient();
    

    Subscribe to its events to be notified when the radio settings change:

    Cat.Tuned += TunedEventHandler;
    Cat.ModeChanged += ModeChangedEventHandler;
    Cat.StatusChanged += StatusChangedEventHandler;
    

    Select Rig1 or Rig2, depending on the user settings:

    Cat.RigNo = (int)settings.RigNo;
    

    Enable the object only when your plugin is activated, and disable it as soon as the plugin is deactivated. Remember that the OmniRig engine requires exclusive access to the COM port and thus should be turned off when not in use:

    private void SetActive(bool value)
    {
      Cat.Active = value;
      ...
    }
    

    When the OmniRigClient object is active, use its methods and properties to read and change the radio settings:

    //read frequency
    var current_frequency = Cat.RxFrequency;
    
    //set frequency
    Cat.RxFrequency = (int)new_frequency;
    

    RingBuffer

    RingBuffer is a thread-safe buffer of the FIFO type for the floating point values. This class is used in all places in the plugins when the I/Q or audio data are received and consumed at different times, in different block sizes, and often on different threads.

    One example is a radio-interfacing plugin. The thread on which the plugin talks to the radio cannot be used for data processing, the code that receives the sampled data just writes the samples to the ring buffer and returns. Another thread reads those samples from the buffer and processes them. An example of such code is available in the Afedri Plugin.

    SlidingMax and SlidingMin

    These two classes implement the fast sliding minimum/maximum algorithm. Create an instance of the class like this:

    var max = new SlidingMax(2 * maxDelay + 1);
    

    The parameter passed to the constructor is filter length that is computed from the desired filter delay, maxDelay, expressed in samples.

    The object created above may be used to process the values online (one sample at a time):

    var filteredValue = max.Process(inputValue);
    

    or to filter an array of floating point values in-place:

    max.FilterArrayInplace(float_array);
    
    Back to top Generated by DocFX