mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-13 00:57:31 -05:00
Updates complex decimation filters to use either scalar or vector optimal implementation. (#1148)
Updates HackRF native byte buffers with automatic DC removal. Creates new half-band channel source to replace the cic-based channel source in the heterodyne channelizer. Resolves issue in RTL native buffer processing. Creates a CACHED thread pool and reduces the number of threads for the SCHEDULED thread pool to 2. Changes each of the channel source implementations to use a dedicated thread (Dispatcher class) instead of the scheduled thread pool, to reduce the amount of buffered/retained samples objects in memory. Lots of other small changes and tweaks. Removes last of Reusable buffer code. Updates DFTProcessor to use a single thread scheduled executor. Updates Polyphase channelizer to use threads instead of an executor. Removes a number of unused classes. Commenting out window calibration. Scalar is faster in both implementations. Adds vector and scalar implementations for windows. Repackages window and window type classes. WIP adds vector implementations for Airspy non-interleaved buffer iterator and calibration plugins for both interleaved and non-interleaved buffer iterators. WIP adds vector implementations for Airspy interleaved buffer iterator. Work on developing scalar and vector implementations of airspy sample conversion and airspy native buffer processing. Introduces new INativeBuffer framework where tuner raw samples are retained in their native (compressed) format until when they're needed and they are converted on-demand into smaller complex sample buffers to aid garbage collection. Removes all usage of ReusableComplexBuffers and ReusableChannelREsultsBuffers. Updates polyphase channelizer to be more efficient in output channel processing. Updates ViterbiDecoder to change Map usage to array for memory efficiency. Updates channel processors to used scalar implementation for gain and mixer, until the polyphase channel results can be fixed to be a multiple of the SIMD lane width. Resolves final changes for the conversion from interleaved sample arrays to complexsamples buffers for distributing baseband samples to channels and processing chains. Updates tuner channel sources and all decoders to receive new complex samples buffers, removing the previous reusable complex buffers as the default baseband samples buffer object. Still to resolve is the ComplexMixer implementations. Needs validation and a calibration implementation with an update to the complex mixer factory. Updates the DMR decoder to use optimal (scalar vs vector) implementations of filter and AGC controls. Updates the AM demodulator for separated I&Q float sample processing. Code review and testing changes. updates build script for runtime configuration. Integrates calibration manager with main sdrtrunk application. Updates NBFMDecoder to use new optimized FIR and Decimation filter options. Updates real half-band decimation filters to select the optimal (scalar vs vector) implementation. Adds calibration manager plugins. Adds calibration manager and plugins to determine the optimal (scalar vs vector) implementation type to use for various classes of dsp components. Adds vector squelching FM demodulator and cleans up the IIR filter and power squelch to use all floats. Adds vector implementation of FM demodulator. This introduces a new interface that accepts i/q samples as two separate float arrays, versus an interleaved array, which enables more efficient processing. This new complex sample interface will be used across the codebase eventually. Adds ComplexOscillator and a SIMD version VectorComplexOscillator. Deprecates the LowPhaseNoiseOscillator because the ComplexOscillator is about 35% faster. The vectorized version is about 43% faster when SIMD lane width is 8x. Adds new vector complex filters and new real oscillators. Note: the RealOscillator using straight Java math sin() function is 3x slower than the existing legacy implementation. Will replace this with a complex oscillator that only uses the real part. Updates ComplexFIRFilter to be 50% more efficient. Adds 23 tap filter implementations. Will not add 63-tap implementations because they are slower than the default vector implementation. Adds 15-tap customized vector half band filters. Adds initial custom complex vector half-band decimation filters for 11-tap and 15-tap decimation filters.. Adds vector complex half band filter implementations. Note: performance of the generic 64-bit 2-lane filter is horrible and is not included. Optimizes complex half-band filter to be 27% more efficient. Implements vectorized real half-band filter implementations. Implements a new FIR filter class that processes sample buffers instead of single sample at a time. Refactored all of the calibratons to be time-duration based. Testing on windows i7 gen 3 CPU showed that it took 1000x longer for one of the calibrations to run, versus my AMD 5800x. Changed gradle jvm args so that the windows plaf export is always applied. Updates gradle build script to ensure incubator module is included and to cleanup javafx library management for building runtime images. Testing tweaks and changes. Adds logging limiter to dispatcher for buffer overflows Adds User Preferences editor for performing Vector/SIMD calibrations and new startup dialog prompting the user to perform calibrations. Updates calibration manager and all calibration implementations to add warm-up and test phases and to reduce the overall time it takes to calibrate a system. Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
/**
|
||||
* Base Oscillator class.
|
||||
*
|
||||
* @see OscillatorFactory
|
||||
*/
|
||||
public abstract class AbstractOscillator implements IOscillator
|
||||
{
|
||||
protected static final double TWO_PI = Math.PI * 2.0d;
|
||||
private double mFrequency;
|
||||
private double mSampleRate;
|
||||
protected float mAnglePerSample;
|
||||
protected float mCurrentPhase = 0.0f;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param frequency in hertz
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
protected AbstractOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
mFrequency = frequency;
|
||||
mSampleRate = sampleRate;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Angle per sample based on current frequency and sample rate
|
||||
* @return angle per sample in radians
|
||||
*/
|
||||
protected float getAnglePerSample()
|
||||
{
|
||||
return mAnglePerSample;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates/calculates the angle per sample from the frequency and sample rate values.
|
||||
*/
|
||||
protected void update()
|
||||
{
|
||||
mAnglePerSample = (float)(TWO_PI * getFrequency() / getSampleRate());
|
||||
}
|
||||
|
||||
/**
|
||||
* Frequency for this oscillator
|
||||
* @return frequency in Hertz
|
||||
*/
|
||||
public double getFrequency()
|
||||
{
|
||||
return mFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the frequency for this oscillator
|
||||
* @param frequency in hertz
|
||||
*/
|
||||
public void setFrequency(double frequency)
|
||||
{
|
||||
mFrequency = frequency;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasFrequency()
|
||||
{
|
||||
return mFrequency != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample rate for this oscillator
|
||||
* @return sample rate in Hertz
|
||||
*/
|
||||
public double getSampleRate()
|
||||
{
|
||||
return mSampleRate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the sample rate for this oscillator
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
public void setSampleRate(double sampleRate)
|
||||
{
|
||||
mSampleRate = sampleRate;
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if this oscillator is enabled, meaning it has a frequency that is not equal to zero.
|
||||
*/
|
||||
public boolean isEnabled()
|
||||
{
|
||||
return mFrequency != 0.0d;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
public class FS4DownConverter
|
||||
{
|
||||
private int mPointer = 0;
|
||||
|
||||
/**
|
||||
* Performs complex frequency conversion at one-fourth (-FS/4) of the sample rate using simple value
|
||||
* assignments.
|
||||
*
|
||||
* See: Digital Signal Processing 3e, Lyons, p.674-675
|
||||
*/
|
||||
public FS4DownConverter()
|
||||
{
|
||||
}
|
||||
|
||||
public float[] mixComplex(float[] samples)
|
||||
{
|
||||
float real;
|
||||
int pointer = mPointer;
|
||||
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
switch(pointer)
|
||||
{
|
||||
case 0:
|
||||
//no-op
|
||||
break;
|
||||
case 1:
|
||||
real = samples[x];
|
||||
samples[x] = samples[x + 1];
|
||||
samples[x + 1] = -real;
|
||||
break;
|
||||
case 2:
|
||||
samples[x] = -samples[x];
|
||||
samples[x + 1] = -samples[x + 1];
|
||||
break;
|
||||
case 3:
|
||||
real = samples[x];
|
||||
samples[x] = -samples[x + 1];
|
||||
samples[x + 1] = real;
|
||||
break;
|
||||
}
|
||||
|
||||
pointer++;
|
||||
pointer %= 4;
|
||||
}
|
||||
|
||||
mPointer = pointer;
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
public ComplexSamples mixComplex(ComplexSamples samples)
|
||||
{
|
||||
float temp;
|
||||
int pointer = mPointer;
|
||||
|
||||
float[] i = samples.i();
|
||||
float[] q = samples.q();;
|
||||
|
||||
for(int x = 0; x < i.length; x++)
|
||||
{
|
||||
switch(pointer)
|
||||
{
|
||||
case 0:
|
||||
//no-op
|
||||
break;
|
||||
case 1:
|
||||
temp = i[x];
|
||||
i[x] = q[x];
|
||||
q[x] = -temp;
|
||||
break;
|
||||
case 2:
|
||||
i[x] = -i[x];
|
||||
q[x] = -q[x];
|
||||
break;
|
||||
case 3:
|
||||
temp = i[x];
|
||||
i[x] = -q[x];
|
||||
q[x] = temp;
|
||||
break;
|
||||
}
|
||||
|
||||
pointer++;
|
||||
pointer %= 4;
|
||||
}
|
||||
|
||||
mPointer = pointer;
|
||||
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
/*******************************************************************************
|
||||
* sdr-trunk
|
||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with this program.
|
||||
* If not, see <http://www.gnu.org/licenses/>
|
||||
*
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class FS4UpConverter
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(FS4UpConverter.class);
|
||||
|
||||
private int mPointer = 0;
|
||||
|
||||
/**
|
||||
* Performs complex up-sampling by one-fourth (+FS/4) of the sample rate using simple value assignments.
|
||||
*
|
||||
* See: Digital Signal Processing 3e, Lyons, p.674-675
|
||||
*/
|
||||
public FS4UpConverter()
|
||||
{
|
||||
}
|
||||
|
||||
public float[] mixComplex(float[] samples)
|
||||
{
|
||||
float real;
|
||||
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
switch(mPointer)
|
||||
{
|
||||
case 0:
|
||||
//no-op
|
||||
break;
|
||||
case 1:
|
||||
real = samples[x];
|
||||
samples[x] = -samples[x + 1];
|
||||
samples[x + 1] = real;
|
||||
break;
|
||||
case 2:
|
||||
samples[x] = -samples[x];
|
||||
samples[x + 1] = -samples[x + 1];
|
||||
break;
|
||||
case 3:
|
||||
real = samples[x];
|
||||
samples[x] = samples[x + 1];
|
||||
samples[x + 1] = -real;
|
||||
break;
|
||||
}
|
||||
|
||||
mPointer++;
|
||||
|
||||
if(mPointer >= 4)
|
||||
{
|
||||
mPointer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
|
||||
public interface IComplexOscillator extends IOscillator
|
||||
{
|
||||
/**
|
||||
* Generates the specified number of complex samples into an interleaved sample array.
|
||||
* @param sampleCount number of complex samples to generate.
|
||||
* @return generated samples where each sample is interleaved: I0, Q0, I1, Q1 ...
|
||||
*/
|
||||
float[] generate(int sampleCount);
|
||||
|
||||
/**
|
||||
* Generates the specified number of complex samples into a complex samples object.
|
||||
* @param sampleCount number of complex samples to generate.
|
||||
* @return generated samples
|
||||
*/
|
||||
ComplexSamples generateComplexSamples(int sampleCount);
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
public interface IOscillator
|
||||
{
|
||||
/**
|
||||
* Frequency of the tone being generated by the oscillator
|
||||
* @return frequency in hertz
|
||||
*/
|
||||
double getFrequency();
|
||||
|
||||
/**
|
||||
* Sets the frequency of the tone being generated by the oscillator.
|
||||
* @param frequency in hertz
|
||||
*/
|
||||
void setFrequency(double frequency);
|
||||
|
||||
/**
|
||||
* Indicates if the current frequency value is non-zero.
|
||||
*/
|
||||
boolean hasFrequency();
|
||||
|
||||
/**
|
||||
* Sample rate of the oscillator
|
||||
* @return sample rate in hertz
|
||||
*/
|
||||
double getSampleRate();
|
||||
|
||||
/**
|
||||
* Sets the sample rate of the oscillator
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
void setSampleRate(double sampleRate);
|
||||
|
||||
|
||||
/**
|
||||
* Indicates if this oscillator is enabled, meaning that it has a non-zero frequency value.
|
||||
* @return true if the frequency value is non-zero
|
||||
*/
|
||||
boolean isEnabled();
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
public interface IRealOscillator extends IOscillator
|
||||
{
|
||||
/**
|
||||
* Generates the specified number of real samples into a sample array.
|
||||
* @param sampleCount number of samples to generate and length of the resulting float array.
|
||||
* @return generated samples
|
||||
*/
|
||||
float[] generate(int sampleCount);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationManager;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Factory class for creating real and complex oscillators.
|
||||
*
|
||||
* Uses the CalibrationManager to determine the optimal (scalar vs vector) oscillator implementation type.
|
||||
*/
|
||||
public class OscillatorFactory
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(OscillatorFactory.class);
|
||||
|
||||
/**
|
||||
* Constructs an optimal implementation of a real sample oscillator using calibration data when available.
|
||||
* @param frequency of the oscillator
|
||||
* @param sampleRate of the oscillator
|
||||
* @return constructed oscillator
|
||||
*/
|
||||
public static IRealOscillator getRealOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
Implementation operation = CalibrationManager.getInstance().getImplementation(CalibrationType.OSCILLATOR_REAL);
|
||||
|
||||
switch(operation)
|
||||
{
|
||||
case VECTOR_SIMD_PREFERRED:
|
||||
return new VectorRealOscillator(frequency, sampleRate);
|
||||
case SCALAR:
|
||||
default:
|
||||
return new ScalarRealOscillator(frequency, sampleRate);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an optimal implementation of a complex sample oscillator using calibration data when available.
|
||||
* @param frequency of the oscillator
|
||||
* @param sampleRate of the oscillator
|
||||
* @return constructed oscillator
|
||||
*/
|
||||
public static IComplexOscillator getComplexOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
Implementation operation = CalibrationManager.getInstance().getImplementation(CalibrationType.OSCILLATOR_COMPLEX);
|
||||
|
||||
switch(operation)
|
||||
{
|
||||
case VECTOR_SIMD_PREFERRED:
|
||||
return new VectorComplexOscillator(frequency, sampleRate);
|
||||
case SCALAR:
|
||||
default:
|
||||
return new ScalarComplexOscillator(frequency, sampleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
|
||||
/**
|
||||
* Scalar implementation of a complex oscillator.
|
||||
*/
|
||||
public class ScalarComplexOscillator extends AbstractOscillator implements IComplexOscillator
|
||||
{
|
||||
private float mCosineAngle;
|
||||
private float mSineAngle;
|
||||
private float mPreviousInphase = 1.0f;
|
||||
private float mPreviousQuadrature = 0.0f;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param frequency in hertz
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
public ScalarComplexOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
super(frequency, sampleRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the internal values after a frequency or sample rate change
|
||||
*/
|
||||
@Override
|
||||
protected void update()
|
||||
{
|
||||
super.update();
|
||||
float angle = getAnglePerSample();
|
||||
mCosineAngle = (float)FastMath.cos(getAnglePerSample());
|
||||
mSineAngle = (float)FastMath.sin(getAnglePerSample());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] generate(int sampleCount)
|
||||
{
|
||||
float[] samples = new float[sampleCount * 2];
|
||||
float cosineAngle = (float)FastMath.cos(getAnglePerSample());
|
||||
float sineAngle = (float)FastMath.sin(getAnglePerSample());
|
||||
|
||||
float previousInphase, previousQuadrature, gain;
|
||||
|
||||
previousInphase = mPreviousInphase;
|
||||
previousQuadrature = mPreviousQuadrature;
|
||||
|
||||
int gainPointer = 0;
|
||||
|
||||
for(int samplePointer = 0; samplePointer < samples.length; samplePointer += 2)
|
||||
{
|
||||
if(++gainPointer % 10 == 0)
|
||||
{
|
||||
gainPointer = 0;
|
||||
gain = (3.0f - ((previousInphase * previousInphase) + (previousQuadrature * previousQuadrature))) / 2.0f;
|
||||
|
||||
samples[samplePointer] = ((previousInphase * cosineAngle) - (previousQuadrature * sineAngle)) * gain;
|
||||
samples[samplePointer + 1] = ((previousInphase * sineAngle) + (previousQuadrature * cosineAngle)) * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[samplePointer] = ((previousInphase * cosineAngle) - (previousQuadrature * sineAngle));
|
||||
samples[samplePointer + 1] = ((previousInphase * sineAngle) + (previousQuadrature * cosineAngle));
|
||||
}
|
||||
|
||||
previousInphase = samples[samplePointer];
|
||||
previousQuadrature = samples[samplePointer + 1];
|
||||
}
|
||||
|
||||
mPreviousInphase = previousInphase;
|
||||
mPreviousQuadrature = previousQuadrature;
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples generateComplexSamples(int sampleCount)
|
||||
{
|
||||
float[] iSamples = new float[sampleCount];
|
||||
float[] qSamples = new float[sampleCount];
|
||||
float cosineAngle = (float)FastMath.cos(getAnglePerSample());
|
||||
float sineAngle = (float)FastMath.sin(getAnglePerSample());
|
||||
|
||||
float previousInphase, previousQuadrature, gain;
|
||||
|
||||
previousInphase = mPreviousInphase;
|
||||
previousQuadrature = mPreviousQuadrature;
|
||||
|
||||
int gainPointer = 0;
|
||||
|
||||
for(int samplePointer = 0; samplePointer < iSamples.length; samplePointer++)
|
||||
{
|
||||
if(++gainPointer % 100 == 0)
|
||||
{
|
||||
gainPointer = 0;
|
||||
gain = (3.0f - ((previousInphase * previousInphase) + (previousQuadrature * previousQuadrature))) / 2.0f;
|
||||
|
||||
iSamples[samplePointer] = ((previousInphase * cosineAngle) - (previousQuadrature * sineAngle)) * gain;
|
||||
qSamples[samplePointer] = ((previousInphase * sineAngle) + (previousQuadrature * cosineAngle)) * gain;
|
||||
}
|
||||
else
|
||||
{
|
||||
iSamples[samplePointer] = ((previousInphase * cosineAngle) - (previousQuadrature * sineAngle));
|
||||
qSamples[samplePointer] = ((previousInphase * sineAngle) + (previousQuadrature * cosineAngle));
|
||||
}
|
||||
|
||||
previousInphase = iSamples[samplePointer];
|
||||
previousQuadrature = qSamples[samplePointer];
|
||||
}
|
||||
|
||||
mPreviousInphase = previousInphase;
|
||||
mPreviousQuadrature = previousQuadrature;
|
||||
|
||||
return new ComplexSamples(iSamples, qSamples);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
public class ScalarRealOscillator extends AbstractOscillator implements IRealOscillator
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param frequency in hertz
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
public ScalarRealOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
super(frequency, sampleRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] generate(int sampleCount)
|
||||
{
|
||||
float[] samples = new float[sampleCount];
|
||||
|
||||
for(int samplePointer = 0; samplePointer < sampleCount; samplePointer++)
|
||||
{
|
||||
mCurrentPhase += mAnglePerSample;
|
||||
mCurrentPhase %= TWO_PI;
|
||||
samples[samplePointer] = (float)Math.sin(mCurrentPhase);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.vector.VectorUtilities;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Complex oscillator that uses JDK17 SIMD vector operations to generate complex sample arrays.
|
||||
*
|
||||
* Note: this class uses a bank of oscillators that are each rotated synchronously, where the oscillator is similar to
|
||||
* the ComplexOscillator class, but where each oscillator is offset in phase by one sample more than the previous and
|
||||
* the entire bank is rotated at the sample phase times the SIMD lane width for each sample generation increment.
|
||||
*/
|
||||
public class VectorComplexOscillator extends AbstractOscillator implements IComplexOscillator
|
||||
{
|
||||
private static final Logger mLog = LoggerFactory.getLogger(VectorComplexOscillator.class);
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_PREFERRED;
|
||||
|
||||
private float[] mPreviousInphases;
|
||||
private float[] mPreviousQuadratures;
|
||||
private float[] mGainInitials; //Set to 3.0f as the first constant in the gain calculation
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param frequency in hertz
|
||||
* @param sampleRate in hertz
|
||||
*/
|
||||
public VectorComplexOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
super(frequency, sampleRate);
|
||||
|
||||
mGainInitials = new float[VECTOR_SPECIES.length()];
|
||||
Arrays.fill(mGainInitials, 3.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void update()
|
||||
{
|
||||
super.update();
|
||||
|
||||
float cosineAngle = (float)FastMath.cos(getAnglePerSample());
|
||||
float sineAngle = (float)FastMath.sin(getAnglePerSample());
|
||||
|
||||
if(mPreviousInphases == null || mPreviousQuadratures == null)
|
||||
{
|
||||
mPreviousInphases = new float[VECTOR_SPECIES.length()];
|
||||
mPreviousQuadratures = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
mPreviousInphases[0] = 1.0f;
|
||||
}
|
||||
|
||||
float gain;
|
||||
|
||||
//Setup the previous sample arrays where each index is offset one sample of rotation from the previous sample.
|
||||
// We don't touch index 0 so that it can maintain the previous phase offset and all other indices are updated
|
||||
// relative to index 0.
|
||||
for(int x = 1; x < VECTOR_SPECIES.length(); x++)
|
||||
{
|
||||
gain = (3.0f - ((mPreviousInphases[x - 1] * mPreviousInphases[x - 1]) +
|
||||
(mPreviousQuadratures[x - 1] * mPreviousQuadratures[x - 1]))) / 2.0f;
|
||||
mPreviousInphases[x] = ((mPreviousInphases[x - 1] * cosineAngle) - (mPreviousQuadratures[x - 1] * sineAngle)) * gain;
|
||||
mPreviousQuadratures[x] = ((mPreviousInphases[x - 1] * sineAngle) + (mPreviousQuadratures[x -1] * cosineAngle)) * gain;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates complex samples.
|
||||
* @param sampleCount number of samples to generate and length of the resulting float array.
|
||||
* @return generated samples
|
||||
*/
|
||||
@Override public float[] generate(int sampleCount)
|
||||
{
|
||||
if(sampleCount % VECTOR_SPECIES.length() != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Requested sample count [" + sampleCount +
|
||||
"] must be a power of 2 and a multiple of the SIMD lane width [" + VECTOR_SPECIES.length() + "]");
|
||||
}
|
||||
|
||||
float[] samples = new float[sampleCount * 2];
|
||||
FloatVector previousInphase = FloatVector.fromArray(VECTOR_SPECIES, mPreviousInphases, 0);
|
||||
FloatVector previousQuadrature = FloatVector.fromArray(VECTOR_SPECIES, mPreviousQuadratures, 0);
|
||||
FloatVector gainInitials = FloatVector.fromArray(VECTOR_SPECIES, mGainInitials, 0);
|
||||
|
||||
//Sine and cosine angle per sample, with the rotation angle multiplied by the SIMD lane width
|
||||
float cosAngle = (float)(FastMath.cos(getAnglePerSample() * VECTOR_SPECIES.length()));
|
||||
float sinAngle = (float)(FastMath.sin(getAnglePerSample() * VECTOR_SPECIES.length()));
|
||||
|
||||
FloatVector gain, inphase, quadrature;
|
||||
|
||||
int gainCounter = 0;
|
||||
|
||||
for(int samplePointer = 0; samplePointer < sampleCount; samplePointer += VECTOR_SPECIES.length())
|
||||
{
|
||||
if(++gainCounter % 10 == 0)
|
||||
{
|
||||
gain = gainInitials.sub(previousInphase.pow(2.0f).add(previousQuadrature.pow(2.0f))).div(2.0f);
|
||||
inphase = previousInphase.mul(cosAngle).sub(previousQuadrature.mul(sinAngle)).mul(gain);
|
||||
quadrature = previousInphase.mul(sinAngle).add(previousQuadrature.mul(cosAngle)).mul(gain);
|
||||
}
|
||||
else
|
||||
{
|
||||
inphase = previousInphase.mul(cosAngle).sub(previousQuadrature.mul(sinAngle));
|
||||
quadrature = previousInphase.mul(sinAngle).add(previousQuadrature.mul(cosAngle));
|
||||
}
|
||||
|
||||
float[] interleaved = VectorUtilities.interleave(inphase, quadrature);
|
||||
System.arraycopy(interleaved, 0, samples, samplePointer * 2, interleaved.length);
|
||||
|
||||
previousInphase = inphase;
|
||||
previousQuadrature = quadrature;
|
||||
}
|
||||
|
||||
previousInphase.intoArray(mPreviousInphases, 0);
|
||||
previousQuadrature.intoArray(mPreviousQuadratures, 0);
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates complex samples.
|
||||
* @param sampleCount number of samples to generate and length of the resulting float array.
|
||||
* @return generated samples
|
||||
*/
|
||||
@Override public ComplexSamples generateComplexSamples(int sampleCount)
|
||||
{
|
||||
if(sampleCount % VECTOR_SPECIES.length() != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Requested sample count [" + sampleCount +
|
||||
"] must be a power of 2 and a multiple of the SIMD lane width [" + VECTOR_SPECIES.length() + "]");
|
||||
}
|
||||
|
||||
float[] iSamples = new float[sampleCount];
|
||||
float[] qSamples = new float[sampleCount];
|
||||
|
||||
FloatVector previousInphase = FloatVector.fromArray(VECTOR_SPECIES, mPreviousInphases, 0);
|
||||
FloatVector previousQuadrature = FloatVector.fromArray(VECTOR_SPECIES, mPreviousQuadratures, 0);
|
||||
FloatVector gainInitials = FloatVector.fromArray(VECTOR_SPECIES, mGainInitials, 0);
|
||||
|
||||
//Sine and cosine angle per sample, with the rotation angle multiplied by the SIMD lane width
|
||||
float cosAngle = (float)(FastMath.cos(getAnglePerSample() * VECTOR_SPECIES.length()));
|
||||
float sinAngle = (float)(FastMath.sin(getAnglePerSample() * VECTOR_SPECIES.length()));
|
||||
|
||||
FloatVector gain, inphase, quadrature;
|
||||
|
||||
for(int samplePointer = 0; samplePointer < sampleCount; samplePointer += VECTOR_SPECIES.length())
|
||||
{
|
||||
gain = gainInitials.sub(previousInphase.pow(2.0f).add(previousQuadrature.pow(2.0f))).div(2.0f);
|
||||
inphase = previousInphase.mul(cosAngle).sub(previousQuadrature.mul(sinAngle)).mul(gain);
|
||||
quadrature = previousInphase.mul(sinAngle).add(previousQuadrature.mul(cosAngle)).mul(gain);
|
||||
|
||||
inphase.intoArray(iSamples, samplePointer);
|
||||
quadrature.intoArray(qSamples, samplePointer);
|
||||
|
||||
previousInphase = inphase;
|
||||
previousQuadrature = quadrature;
|
||||
}
|
||||
|
||||
previousInphase.intoArray(mPreviousInphases, 0);
|
||||
previousQuadrature.intoArray(mPreviousQuadratures, 0);
|
||||
|
||||
return new ComplexSamples(iSamples, qSamples);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.dsp.oscillator;
|
||||
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Real oscillator that uses SIMD vector intrinsics from JDK17+ Project Panama.
|
||||
*/
|
||||
public class VectorRealOscillator extends AbstractOscillator implements IRealOscillator
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_PREFERRED;
|
||||
private float[] mOffsets;
|
||||
|
||||
public VectorRealOscillator(double frequency, double sampleRate)
|
||||
{
|
||||
super(frequency, sampleRate);
|
||||
|
||||
mOffsets = new float[VECTOR_SPECIES.length()];
|
||||
for(int x = 0; x < mOffsets.length; x++)
|
||||
{
|
||||
mOffsets[x] = x + 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float[] generate(int sampleCount)
|
||||
{
|
||||
float[] samples = new float[sampleCount];
|
||||
|
||||
for(int samplePointer = 0; samplePointer < sampleCount; samplePointer += VECTOR_SPECIES.length())
|
||||
{
|
||||
FloatVector generated = FloatVector.fromArray(VECTOR_SPECIES, mOffsets, 0);
|
||||
generated = generated.mul(mAnglePerSample);
|
||||
generated = generated.add(mCurrentPhase);
|
||||
mCurrentPhase = generated.lane(VECTOR_SPECIES.length() - 1);
|
||||
mCurrentPhase %= TWO_PI;
|
||||
generated = generated.lanewise(VectorOperators.SIN);
|
||||
generated.intoArray(samples, samplePointer);
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user