mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Co-authored-by: sheirerd <sheirerd@ainfosec.com>
This commit is contained in:
@@ -125,6 +125,7 @@ dependencies {
|
||||
implementation 'commons-io:commons-io:2.22.0'
|
||||
implementation 'eu.hansolo:charts:1.0.5'
|
||||
implementation 'io.github.dsheirer:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jairspy:1.0.12' //Airspy tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jhydrasdr:1.1.3' //HydraSDR tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
||||
implementation 'io.github.dsheirer:usb4java-native-libraries:1.3.3' //OSX & Windows aarch64 native libs
|
||||
|
||||
@@ -9,6 +9,14 @@ dependencyResolutionManagement {
|
||||
mavenCentral()
|
||||
maven { url 'https://jitpack.io' }
|
||||
|
||||
//jAirspy is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||
ivy {
|
||||
url = 'https://github.com/DSheirer/jAirspy/releases/download'
|
||||
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
|
||||
metadataSources { artifact() }
|
||||
content { includeModule 'io.github.dsheirer', 'jairspy' }
|
||||
}
|
||||
|
||||
//jHydraSDR is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||
ivy {
|
||||
url = 'https://github.com/DSheirer/jHydraSDR/releases/download'
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Interface to convert samples from a byte buffer to an array of shorts.
|
||||
*/
|
||||
public interface ISampleConverter
|
||||
{
|
||||
/**
|
||||
* Converts the byte samples contained in the byte buffer to their short-valued representation
|
||||
* @param buffer of samples
|
||||
* @return converted samples
|
||||
*/
|
||||
short[] convert(ByteBuffer buffer);
|
||||
|
||||
/**
|
||||
* Current DC average of the samples that have been processed thus far.
|
||||
*/
|
||||
float getAverageDc();
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.hilbert.HilbertTransform;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Base complex samples iterator for raw sample buffers. Incorporates a Hilbert transform filter
|
||||
* for converting the real sample array to complex samples.
|
||||
* @param <T> either ComplexSamples or InterleavedComplexSamples
|
||||
*/
|
||||
public abstract class SampleBufferIterator<T> implements Iterator<T>
|
||||
{
|
||||
protected static final float SCALE_SIGNED_12_BIT_TO_FLOAT = 1.0f / 2048.0f;
|
||||
protected static final int FRAGMENT_SIZE = 4096;
|
||||
protected static final float[] COEFFICIENTS =
|
||||
HilbertTransform.convertHalfBandToHilbert(HilbertTransform.HALF_BAND_FILTER_47_TAP);
|
||||
|
||||
public static final int I_OVERLAP = 11;
|
||||
public static final int Q_OVERLAP = 23;
|
||||
|
||||
protected float[] mIBuffer = new float[FRAGMENT_SIZE + I_OVERLAP];
|
||||
protected float[] mQBuffer = new float[FRAGMENT_SIZE + Q_OVERLAP];
|
||||
protected short[] mSamples;
|
||||
protected int mSamplesPointer = 0;
|
||||
protected float mAverageDc;
|
||||
private long mTimestamp;
|
||||
private float mSamplesPerMillisecond;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param samples from the tuner.
|
||||
* @param residualI samples from previous buffer
|
||||
* @param residualQ samples from previous buffer
|
||||
* @param averageDc as measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleBufferIterator(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp,
|
||||
float samplesPerMillisecond)
|
||||
{
|
||||
if(residualI.length != I_OVERLAP || residualQ.length != Q_OVERLAP)
|
||||
{
|
||||
throw new IllegalArgumentException("Residual I length [" + residualI.length +
|
||||
"] must be " + I_OVERLAP + " and Residual Q length [" + residualQ.length +
|
||||
"] must be " + Q_OVERLAP);
|
||||
}
|
||||
|
||||
int requiredInterval = FRAGMENT_SIZE * 2; //requires 4 bytes (2 samples) per fragment
|
||||
|
||||
if(samples.length % requiredInterval != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Samples short array length [" + mSamples.length +
|
||||
"]must be an integer multiple of " + requiredInterval);
|
||||
}
|
||||
|
||||
mAverageDc = averageDc;
|
||||
mSamples = samples;
|
||||
mTimestamp = timestamp;
|
||||
mSamplesPerMillisecond = samplesPerMillisecond;
|
||||
|
||||
//Transfer and scale the residual I & Q samples from the previous buffer
|
||||
for(int i = 0; i < residualI.length; i++)
|
||||
{
|
||||
mIBuffer[i] = scale(residualI[i], averageDc);
|
||||
}
|
||||
|
||||
for(int q = 0; q < residualQ.length; q++)
|
||||
{
|
||||
mQBuffer[q] = scale(residualQ[q], averageDc);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the timestamp for a samples buffer fragment based on where it is extracted from relative to the
|
||||
* native buffer starting timestamp.
|
||||
* @param samplesPointer for the start of the fragment. Note: this value will be divided by 2 to account for I/Q sample pairs.
|
||||
* @return timestamp adjusted to the fragment or sub-buffer start sample.
|
||||
*/
|
||||
protected long getFragmentTimestamp(int samplesPointer)
|
||||
{
|
||||
return mTimestamp + (long)(samplesPointer / 2 / mSamplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return mSamplesPointer < mSamples.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scales the short sample value to a 12-bit floating point and subtracts the average DC offset
|
||||
* @param value to scale
|
||||
* @param averageDc detected
|
||||
* @return scaled and normalized sample
|
||||
*/
|
||||
public static float scale(short value, float averageDc)
|
||||
{
|
||||
return ((float)value - 2048) * SCALE_SIGNED_12_BIT_TO_FLOAT - averageDc;
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Scalar implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleBufferIteratorScalar extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
int offset = mSamplesPointer;
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
mIBuffer[x + I_OVERLAP] = scale(mSamples[offset++], mAverageDc);
|
||||
mQBuffer[x + Q_OVERLAP] = scale(mSamples[offset++], mAverageDc);
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
//The i line is a simple delay line, so we'll just copy those as a new array here.
|
||||
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
|
||||
float accumulator;
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = 0;
|
||||
|
||||
for(int tap = 0; tap < COEFFICIENTS.length; tap++)
|
||||
{
|
||||
accumulator += COEFFICIENTS[tap] * mQBuffer[x + tap];
|
||||
}
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
q[x] = accumulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
i[x] = -i[x];
|
||||
q[x] = -accumulator;
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleBufferIteratorVector128Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
//The i line is a simple delay line, so we'll just copy those as a new array here.
|
||||
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
|
||||
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
|
||||
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
|
||||
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
|
||||
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
|
||||
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
|
||||
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
|
||||
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
i[x] = -i[x];
|
||||
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleBufferIteratorVector256Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
//The i line is a simple delay line, so we'll just copy those as a new array here.
|
||||
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
|
||||
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
i[x] = -i[x];
|
||||
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleBufferIteratorVector512Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float[] mFilterPart2;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
//Arrange the last 8 taps of the filter into the higher indices of an array to align for SIMD
|
||||
mFilterPart2 = new float[16];
|
||||
System.arraycopy(COEFFICIENTS, 16, mFilterPart2, 8, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
//The i line is a simple delay line, so we'll just copy those as a new array here.
|
||||
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, mFilterPart2, 0);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
i[x] = -i[x];
|
||||
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleBufferIteratorVector64Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
//The i line is a simple delay line, so we'll just copy those as a new array here.
|
||||
float[] i = Arrays.copyOf(mIBuffer, FRAGMENT_SIZE);
|
||||
float[] q = new float[FRAGMENT_SIZE];
|
||||
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 2);
|
||||
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
|
||||
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 6);
|
||||
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
|
||||
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 10);
|
||||
FloatVector f7 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
|
||||
FloatVector f8 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 14);
|
||||
FloatVector f9 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
|
||||
FloatVector f10 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 18);
|
||||
FloatVector f11 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
|
||||
FloatVector f12 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 22);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 2), accumulator);
|
||||
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
|
||||
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 6), accumulator);
|
||||
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 10), accumulator);
|
||||
accumulator = f7.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
|
||||
accumulator = f8.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 14), accumulator);
|
||||
accumulator = f9.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
|
||||
accumulator = f10.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 18), accumulator);
|
||||
accumulator = f11.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
|
||||
accumulator = f12.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 22), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
q[x] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
i[x] = -i[x];
|
||||
q[x] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new ComplexSamples(i, q, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
/**
|
||||
* Scalar implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleInterleavedBufferIteratorScalar extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleInterleavedBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
mIBuffer[x + I_OVERLAP] = scale(mSamples[offset++], mAverageDc);
|
||||
mQBuffer[x + Q_OVERLAP] = scale(mSamples[offset++], mAverageDc);
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
float[] samples = new float[FRAGMENT_SIZE * 2];
|
||||
|
||||
float accumulator;
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = 0;
|
||||
|
||||
for(int tap = 0; tap < COEFFICIENTS.length; tap++)
|
||||
{
|
||||
accumulator += COEFFICIENTS[tap] * mQBuffer[x + tap];
|
||||
}
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
samples[2 * x] = mIBuffer[x];
|
||||
samples[2 * x + 1] = accumulator;
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[2 * x] = -mIBuffer[x];
|
||||
samples[2 * x + 1] = -accumulator;
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleInterleavedBufferIteratorVector128Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleInterleavedBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
float[] samples = new float[FRAGMENT_SIZE * 2];
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
|
||||
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
|
||||
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
|
||||
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
|
||||
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
|
||||
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
|
||||
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
|
||||
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
samples[2 * x] = mIBuffer[x];
|
||||
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[2 * x] = -mIBuffer[x];
|
||||
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleInterleavedBufferIteratorVector256Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleInterleavedBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
float[] samples = new float[FRAGMENT_SIZE * 2];
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
|
||||
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
samples[2 * x] = mIBuffer[x];
|
||||
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[2 * x] = -mIBuffer[x];
|
||||
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,122 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleInterleavedBufferIteratorVector512Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float[] mFilterPart2;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleInterleavedBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
|
||||
//Arrange the last 8 taps of the filter into the higher indices of an array to align for SIMD
|
||||
mFilterPart2 = new float[16];
|
||||
System.arraycopy(COEFFICIENTS, 16, mFilterPart2, 8, 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
float[] samples = new float[FRAGMENT_SIZE * 2];
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, mFilterPart2, 0);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
samples[2 * x] = mIBuffer[x];
|
||||
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[2 * x] = -mIBuffer[x];
|
||||
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class SampleInterleavedBufferIteratorVector64Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the device, either packed or unpacked.
|
||||
* @param residualI samples from last buffer
|
||||
* @param residualQ samples from last buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param samplesPerMillisecond to calculate sub-buffer fragment timestamps
|
||||
*/
|
||||
public SampleInterleavedBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
if(mSamplesPointer >= mSamples.length)
|
||||
{
|
||||
throw new IllegalStateException("End of buffer exceeded");
|
||||
}
|
||||
|
||||
long timestamp = getFragmentTimestamp(mSamplesPointer);
|
||||
int offset = mSamplesPointer;
|
||||
int fragmentPointer = 0;
|
||||
float[] scaledSamples = new float[VECTOR_SPECIES.length()];
|
||||
|
||||
while(fragmentPointer < FRAGMENT_SIZE)
|
||||
{
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y++)
|
||||
{
|
||||
//Couldn't figure out an easy way to load as a short vector and recast to a float vector
|
||||
scaledSamples[y] = mSamples[offset++];
|
||||
}
|
||||
|
||||
//SIMD scaling operation
|
||||
FloatVector.fromArray(VECTOR_SPECIES, scaledSamples, 0)
|
||||
.sub(2048.0f)
|
||||
.mul(SCALE_SIGNED_12_BIT_TO_FLOAT)
|
||||
.sub(mAverageDc)
|
||||
.intoArray(scaledSamples, 0);
|
||||
|
||||
for(int y = 0; y < VECTOR_SPECIES.length(); y += 2)
|
||||
{
|
||||
mIBuffer[fragmentPointer + I_OVERLAP] = scaledSamples[y];
|
||||
mQBuffer[fragmentPointer++ + Q_OVERLAP] = scaledSamples[y + 1];
|
||||
}
|
||||
}
|
||||
|
||||
mSamplesPointer = offset;
|
||||
|
||||
float[] samples = new float[FRAGMENT_SIZE * 2];
|
||||
FloatVector accumulator;
|
||||
FloatVector f1 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 0);
|
||||
FloatVector f2 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 2);
|
||||
FloatVector f3 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 4);
|
||||
FloatVector f4 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 6);
|
||||
FloatVector f5 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 8);
|
||||
FloatVector f6 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 10);
|
||||
FloatVector f7 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 12);
|
||||
FloatVector f8 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 14);
|
||||
FloatVector f9 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 16);
|
||||
FloatVector f10 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 18);
|
||||
FloatVector f11 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 20);
|
||||
FloatVector f12 = FloatVector.fromArray(VECTOR_SPECIES, COEFFICIENTS, 22);
|
||||
|
||||
for(int x = 0; x < FRAGMENT_SIZE; x++)
|
||||
{
|
||||
accumulator = FloatVector.zero(VECTOR_SPECIES);
|
||||
accumulator = f1.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x), accumulator);
|
||||
accumulator = f2.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 2), accumulator);
|
||||
accumulator = f3.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 4), accumulator);
|
||||
accumulator = f4.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 6), accumulator);
|
||||
accumulator = f5.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 8), accumulator);
|
||||
accumulator = f6.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 10), accumulator);
|
||||
accumulator = f7.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 12), accumulator);
|
||||
accumulator = f8.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 14), accumulator);
|
||||
accumulator = f9.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 16), accumulator);
|
||||
accumulator = f10.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 18), accumulator);
|
||||
accumulator = f11.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 20), accumulator);
|
||||
accumulator = f12.fma(FloatVector.fromArray(VECTOR_SPECIES, mQBuffer, x + 22), accumulator);
|
||||
|
||||
//Perform FS/2 frequency translation on final filtered values ... multiply sequence by 1, -1, etc.
|
||||
if(x % 2 == 0)
|
||||
{
|
||||
samples[2 * x] = mIBuffer[x];
|
||||
samples[2 * x + 1] = accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
else
|
||||
{
|
||||
samples[2 * x] = -mIBuffer[x];
|
||||
samples[2 * x + 1] = -accumulator.reduceLanes(VectorOperators.ADD);
|
||||
}
|
||||
}
|
||||
|
||||
//Copy residual end samples to beginning of buffers for the next iteration
|
||||
System.arraycopy(mIBuffer, FRAGMENT_SIZE, mIBuffer, 0, I_OVERLAP);
|
||||
System.arraycopy(mQBuffer, FRAGMENT_SIZE, mQBuffer, 0, Q_OVERLAP);
|
||||
|
||||
return new InterleavedComplexSamples(samples, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2026 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Native buffer scalar implementation for non-packed samples.
|
||||
*/
|
||||
public class SampleNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private final short[] mSamples;
|
||||
private final short[] mResidualI;
|
||||
private final short[] mResidualQ;
|
||||
private final float mAverageDc;
|
||||
private final Implementation mInterleavedImplementation;
|
||||
private final Implementation mNonInterleavedImplementation;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param samples (non-packed) from the device
|
||||
* @param residualI samples from previous buffer
|
||||
* @param residualQ samples from previous buffer
|
||||
* @param averageDc measured
|
||||
* @param timestamp of the buffer
|
||||
* @param interleavedImplementation optimal, scalar vs vector SIMD
|
||||
* @param nonInterleavedImplementation optimal, scalar vs vector SIMD
|
||||
* @param samplesPerMillisecond used to calculate sub-buffer fragment timestamp offsets from the start of this buffer.
|
||||
*/
|
||||
public SampleNativeBuffer(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, Implementation interleavedImplementation,
|
||||
Implementation nonInterleavedImplementation, float samplesPerMillisecond)
|
||||
{
|
||||
super(timestamp, samplesPerMillisecond);
|
||||
|
||||
//Ensure we're an even multiple of the fragment size. Typically, this will be 64k or 128k
|
||||
if(samples.length % SampleBufferIterator.FRAGMENT_SIZE != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Samples short[] length [" + samples.length +
|
||||
"] must be an even multiple of " + SampleBufferIterator.FRAGMENT_SIZE);
|
||||
}
|
||||
|
||||
mSamples = samples;
|
||||
mResidualI = residualI;
|
||||
mResidualQ = residualQ;
|
||||
mAverageDc = averageDc;
|
||||
mInterleavedImplementation = interleavedImplementation;
|
||||
mNonInterleavedImplementation = nonInterleavedImplementation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sampleCount()
|
||||
{
|
||||
return mSamples.length / 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ComplexSamples> iterator()
|
||||
{
|
||||
return switch(mNonInterleavedImplementation)
|
||||
{
|
||||
case VECTOR_SIMD_512 -> new SampleBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_256-> new SampleBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_128-> new SampleBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_64 ->new SampleBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
default -> new SampleBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<InterleavedComplexSamples> iteratorInterleaved()
|
||||
{
|
||||
return switch(mInterleavedImplementation)
|
||||
{
|
||||
case VECTOR_SIMD_512 -> new SampleInterleavedBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_256-> new SampleInterleavedBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_128-> new SampleInterleavedBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_64 ->new SampleInterleavedBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
default -> new SampleInterleavedBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationManager;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Implements a factory for creating SignedByteNativeBuffer instances
|
||||
*/
|
||||
public class SampleNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
private boolean mSamplePacking = false;
|
||||
private short[] mResidualI = new short[SampleBufferIterator.I_OVERLAP];
|
||||
private short[] mResidualQ = new short[SampleBufferIterator.Q_OVERLAP];
|
||||
private ISampleConverter mConverter;
|
||||
private Implementation mInterleavedIteratorImplementation;
|
||||
private Implementation mNonInterleavedIteratorImplementation;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public SampleNativeBufferFactory()
|
||||
{
|
||||
updateConverter();
|
||||
|
||||
mInterleavedIteratorImplementation = CalibrationManager.getInstance()
|
||||
.getImplementation(CalibrationType.SAMPLE_UNPACKED_INTERLEAVED_ITERATOR);
|
||||
mNonInterleavedIteratorImplementation = CalibrationManager.getInstance()
|
||||
.getImplementation(CalibrationType.SAMPLE_UNPACKED_ITERATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample packing places two 12-bit samples into 3 bytes when enabled or
|
||||
* places two 12-bit samples into 4 bytes when disabled.
|
||||
*
|
||||
* @param enabled
|
||||
*/
|
||||
public void setSamplePacking(boolean enabled)
|
||||
{
|
||||
mSamplePacking = enabled;
|
||||
updateConverter();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an optimal sample converter instance.
|
||||
*/
|
||||
private void updateConverter()
|
||||
{
|
||||
if(mSamplePacking)
|
||||
{
|
||||
if(!(mConverter instanceof ScalarPackedSampleConverter))
|
||||
{
|
||||
mConverter = new ScalarPackedSampleConverter();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.SAMPLE_PACKED_CONVERTER);
|
||||
|
||||
if(implementation == Implementation.VECTOR_SIMD_PREFERRED)
|
||||
{
|
||||
if(!(mConverter instanceof VectorUnpackedSampleConverter))
|
||||
{
|
||||
mConverter = new VectorUnpackedSampleConverter();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(!(mConverter instanceof ScalarUnpackedSampleConverter))
|
||||
{
|
||||
mConverter = new ScalarUnpackedSampleConverter();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public INativeBuffer getBuffer(ByteBuffer buffer, long timestamp)
|
||||
{
|
||||
short[] samples = mConverter.convert(buffer);
|
||||
|
||||
INativeBuffer nativeBuffer = new SampleNativeBuffer(samples,
|
||||
Arrays.copyOf(mResidualI, mResidualI.length),
|
||||
Arrays.copyOf(mResidualQ, mResidualQ.length), mConverter.getAverageDc(), timestamp,
|
||||
mInterleavedIteratorImplementation, mNonInterleavedIteratorImplementation, getSamplesPerMillisecond());
|
||||
|
||||
extractResidual(samples);
|
||||
|
||||
return nativeBuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the residual overlap samples needed for continuity in the Hilbert transform filter.
|
||||
* @param samples to extract residual from
|
||||
*/
|
||||
private void extractResidual(short[] samples)
|
||||
{
|
||||
int offset = samples.length - (SampleBufferIterator.I_OVERLAP * 2);
|
||||
|
||||
for(int i = 0; i < SampleBufferIterator.I_OVERLAP; i++)
|
||||
{
|
||||
mResidualI[i] = samples[offset + (2 * i)];
|
||||
}
|
||||
|
||||
offset = samples.length - (SampleBufferIterator.Q_OVERLAP * 2) + 1;
|
||||
|
||||
for(int q = 0; q < SampleBufferIterator.Q_OVERLAP; q++)
|
||||
{
|
||||
mResidualQ[q] = samples[offset + (2 * q)];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Scalar implementation of sample converter for un-packed samples.
|
||||
*/
|
||||
public class ScalarPackedSampleConverter implements ISampleConverter
|
||||
{
|
||||
/**
|
||||
* Manages DC calculations and processing interval
|
||||
*/
|
||||
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
|
||||
|
||||
@Override
|
||||
public short[] convert(ByteBuffer buffer)
|
||||
{
|
||||
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
|
||||
|
||||
int offset = 0;
|
||||
short sample;
|
||||
short[] samples;
|
||||
byte b1, b2, b3;
|
||||
|
||||
samples = new short[buffer.capacity() / 3 * 2];
|
||||
|
||||
if(shouldCalculateDc)
|
||||
{
|
||||
long dcAccumulator = 0;
|
||||
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
b1 = buffer.get(offset++);
|
||||
b2 = buffer.get(offset++);
|
||||
b3 = buffer.get(offset++);
|
||||
|
||||
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
|
||||
samples[x] = sample;
|
||||
dcAccumulator += sample;
|
||||
|
||||
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
|
||||
samples[x] = sample;
|
||||
dcAccumulator += sample;
|
||||
}
|
||||
|
||||
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
|
||||
averageDcNow *= SampleBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
mDcCalculationManager.adjust(averageDcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int x = 0; x < samples.length; x += 2)
|
||||
{
|
||||
b1 = buffer.get(offset++);
|
||||
b2 = buffer.get(offset++);
|
||||
b3 = buffer.get(offset++);
|
||||
|
||||
sample = (short)(((b1 & 0xFF) << 4) | ((b2 & 0xF0) >> 4));
|
||||
samples[x] = sample;
|
||||
|
||||
sample = (short)(((b2 & 0xF) << 8) | (b3 & 0xFF));
|
||||
samples[x] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAverageDc()
|
||||
{
|
||||
return mDcCalculationManager.getAverageDc();
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
byte[] raw = new byte[262144];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(raw);
|
||||
|
||||
ScalarPackedSampleConverter scalar = new ScalarPackedSampleConverter();
|
||||
VectorUnpackedSampleConverter vector = new VectorUnpackedSampleConverter();
|
||||
|
||||
long accumulator = 0;
|
||||
int iterations = 1_000_000;
|
||||
|
||||
System.out.println("Starting ...");
|
||||
long start = System.currentTimeMillis();
|
||||
for(int count = 0; count < iterations; count++)
|
||||
{
|
||||
// short[] samples = scalar.convert(buffer);
|
||||
short[] samples = vector.convert(buffer);
|
||||
accumulator += samples[1];
|
||||
}
|
||||
long elapsed = System.currentTimeMillis() - start;
|
||||
|
||||
System.out.println("Accumulator: " + accumulator);
|
||||
System.out.println("Elapsed: " + (elapsed / 1000.0f));
|
||||
System.out.println("Finished");
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Scalar implementation of sample converter for un-packed samples.
|
||||
*/
|
||||
public class ScalarUnpackedSampleConverter implements ISampleConverter
|
||||
{
|
||||
/**
|
||||
* Manages DC calculations and processing interval
|
||||
*/
|
||||
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
|
||||
|
||||
@Override
|
||||
public short[] convert(ByteBuffer buffer)
|
||||
{
|
||||
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
|
||||
|
||||
int offset = 0;
|
||||
short sample;
|
||||
short[] samples;
|
||||
byte b1, b2;
|
||||
|
||||
samples = new short[buffer.capacity() / 2];
|
||||
|
||||
if(shouldCalculateDc)
|
||||
{
|
||||
long dcAccumulator = 0;
|
||||
|
||||
for(int x = 0; x < samples.length; x++)
|
||||
{
|
||||
b1 = buffer.get(offset++);
|
||||
b2 = buffer.get(offset++);
|
||||
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
|
||||
samples[x] = sample;
|
||||
dcAccumulator += sample;
|
||||
}
|
||||
|
||||
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
|
||||
averageDcNow *= SampleBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
mDcCalculationManager.adjust(averageDcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int x = 0; x < samples.length; x++)
|
||||
{
|
||||
b1 = buffer.get(offset++);
|
||||
b2 = buffer.get(offset++);
|
||||
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
|
||||
samples[x] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAverageDc()
|
||||
{
|
||||
return mDcCalculationManager.getAverageDc();
|
||||
}
|
||||
}
|
||||
@@ -1,129 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 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.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
import jdk.incubator.vector.ShortVector;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Scalar implementation of sample converter for un-packed samples.
|
||||
*/
|
||||
public class VectorUnpackedSampleConverter implements ISampleConverter
|
||||
{
|
||||
private static final VectorSpecies<Short> VECTOR_SPECIES = ShortVector.SPECIES_PREFERRED;
|
||||
|
||||
/**
|
||||
* Manages DC calculations and processing interval
|
||||
*/
|
||||
private DcCorrectionManager mDcCalculationManager = new DcCorrectionManager();
|
||||
|
||||
@Override
|
||||
public short[] convert(ByteBuffer buffer)
|
||||
{
|
||||
boolean shouldCalculateDc = mDcCalculationManager.shouldCalculateDc();
|
||||
|
||||
short sample;
|
||||
short[] samples;
|
||||
byte b1, b2;
|
||||
|
||||
samples = new short[buffer.capacity() / 2];
|
||||
|
||||
int bytesOffset;
|
||||
int rawPointer = 0;
|
||||
ShortVector vector;
|
||||
|
||||
int samplesOffset = 0;
|
||||
|
||||
short[] bytes1 = new short[VECTOR_SPECIES.length()];
|
||||
short[] bytes2 = new short[VECTOR_SPECIES.length()];
|
||||
|
||||
if(shouldCalculateDc)
|
||||
{
|
||||
long dcAccumulator = 0;
|
||||
|
||||
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
|
||||
{
|
||||
bytesOffset = 0;
|
||||
|
||||
while(bytesOffset < VECTOR_SPECIES.length())
|
||||
{
|
||||
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
|
||||
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
|
||||
}
|
||||
|
||||
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
|
||||
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
|
||||
dcAccumulator += vector.reduceLanes(VectorOperators.ADD);
|
||||
vector.intoArray(samples, samplesOffset);
|
||||
}
|
||||
|
||||
for(; samplesOffset < samples.length; samplesOffset++)
|
||||
{
|
||||
b1 = buffer.get(rawPointer++);
|
||||
b2 = buffer.get(rawPointer++);
|
||||
|
||||
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
|
||||
samples[samplesOffset] = sample;
|
||||
dcAccumulator += sample;
|
||||
}
|
||||
|
||||
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
|
||||
averageDcNow *= SampleBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
mDcCalculationManager.adjust(averageDcNow);
|
||||
}
|
||||
else
|
||||
{
|
||||
for(; samplesOffset < VECTOR_SPECIES.loopBound(samples.length); samplesOffset += VECTOR_SPECIES.length())
|
||||
{
|
||||
bytesOffset = 0;
|
||||
|
||||
while(bytesOffset < VECTOR_SPECIES.length())
|
||||
{
|
||||
bytes1[bytesOffset] = (short)(buffer.get(rawPointer++) & 0xFF);
|
||||
bytes2[bytesOffset++] = (short)(buffer.get(rawPointer++) & 0x0F);
|
||||
}
|
||||
|
||||
vector = ShortVector.fromArray(VECTOR_SPECIES, bytes1, 0)
|
||||
.or(ShortVector.fromArray(VECTOR_SPECIES, bytes2, 0).lanewise(VectorOperators.LSHL, 8));
|
||||
vector.intoArray(samples, samplesOffset);
|
||||
}
|
||||
|
||||
for(; samplesOffset < samples.length; samplesOffset++)
|
||||
{
|
||||
b1 = buffer.get(rawPointer++);
|
||||
b2 = buffer.get(rawPointer++);
|
||||
|
||||
sample = (short)(((b2 & 0x0F) << 8) | (b1 & 0xFF));
|
||||
samples[samplesOffset] = sample;
|
||||
}
|
||||
}
|
||||
|
||||
return samples;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getAverageDc()
|
||||
{
|
||||
return mDcCalculationManager.getAverageDc();
|
||||
}
|
||||
}
|
||||
@@ -1,157 +1,102 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2026 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.source.tuner.airspy;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Arrays;
|
||||
import io.github.dsheirer.airspy.PartIdSerialNumber;
|
||||
|
||||
/**
|
||||
* Airspy device information, read from the device by libairspy.
|
||||
*/
|
||||
public class AirspyDeviceInformation
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( AirspyDeviceInformation.class );
|
||||
|
||||
private AirspyTunerController.BoardID mBoardID = AirspyTunerController.BoardID.UNKNOWN;
|
||||
private String mFirmwareVersion = "Unknown";
|
||||
private String mPartNumber = "Unknown";
|
||||
private String mSerialNumber = "Unknown";
|
||||
private final String mBoardName;
|
||||
private final String mFirmwareVersion;
|
||||
private final PartIdSerialNumber mPartIdSerialNumber;
|
||||
|
||||
/**
|
||||
* Airspy Device Information - provides parsing and access to device
|
||||
* information components.
|
||||
* Constructs an instance
|
||||
* @param boardName from the device
|
||||
* @param firmwareVersion string from the device
|
||||
* @param partIdSerialNumber from the device
|
||||
*/
|
||||
public AirspyDeviceInformation()
|
||||
public AirspyDeviceInformation(String boardName, String firmwareVersion, PartIdSerialNumber partIdSerialNumber)
|
||||
{
|
||||
mBoardName = boardName;
|
||||
mFirmwareVersion = firmwareVersion;
|
||||
mPartIdSerialNumber = partIdSerialNumber;
|
||||
}
|
||||
|
||||
public void setBoardID( int boardID )
|
||||
|
||||
public String getBoardName()
|
||||
{
|
||||
mBoardID = AirspyTunerController.BoardID.fromValue( boardID );
|
||||
return mBoardName;
|
||||
}
|
||||
|
||||
public AirspyTunerController.BoardID getBoardID()
|
||||
{
|
||||
return mBoardID;
|
||||
}
|
||||
|
||||
public void setVersion( byte[] data )
|
||||
{
|
||||
if( data != null && data.length > 0 )
|
||||
{
|
||||
/* Truncate the version string if we have a line break - ASCII 10 */
|
||||
for( int x = 0; x < data.length; x++ )
|
||||
{
|
||||
if( data[ x ] == (byte)10 )
|
||||
{
|
||||
mFirmwareVersion = new String( Arrays.copyOf( data, x ) );
|
||||
mFirmwareVersion = mFirmwareVersion.replace( "AirSpy NOS ", "" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mFirmwareVersion = new String( data );
|
||||
mFirmwareVersion = mFirmwareVersion.replace( "AirSpy NOS ", "" );
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error( "Error setting airspy version byte data - null:" +
|
||||
( data == null ? " TRUE" : " FALSE length:" + data.length ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Firmware version, without the "AirSpy NOS " prefix and truncated at the first line break.
|
||||
*/
|
||||
public String getVersion()
|
||||
{
|
||||
return mFirmwareVersion;
|
||||
}
|
||||
|
||||
public void setPartAndSerialNumber( byte[] data )
|
||||
{
|
||||
if( data != null && data.length == 24 )
|
||||
if(mFirmwareVersion == null)
|
||||
{
|
||||
//Note: values are byte-reversed (big-endian) 32-bit chunks
|
||||
StringBuilder part = new StringBuilder();
|
||||
|
||||
part.append( format( data[ 3 ] ) );
|
||||
part.append( format( data[ 2 ] ) );
|
||||
part.append( format( data[ 1 ] ) );
|
||||
part.append( format( data[ 0 ] ) );
|
||||
part.append( format( data[ 7 ] ) );
|
||||
part.append( format( data[ 6 ] ) );
|
||||
part.append( format( data[ 5 ] ) );
|
||||
part.append( format( data[ 4 ] ) );
|
||||
|
||||
mPartNumber = part.toString();
|
||||
|
||||
StringBuilder serial = new StringBuilder();
|
||||
|
||||
//Note: current airspy library only exposes 64-bits of serial number
|
||||
//address space, but appears to have room for 128-bits. We won't
|
||||
//add the first 64 bits if it's zeros
|
||||
if( !( data[ 8 ] == 0 && data[ 9 ] == 0 && data[ 10 ] == 0 && data[ 11 ] == 0 ) )
|
||||
{
|
||||
serial.append( format( data[ 11 ] ) );
|
||||
serial.append( format( data[ 10 ] ) );
|
||||
serial.append( format( data[ 9 ] ) );
|
||||
serial.append( format( data[ 8 ] ) );
|
||||
serial.append( "-" );
|
||||
}
|
||||
|
||||
if( !( data[ 12 ] == 0 && data[ 13 ] == 0 && data[ 14 ] == 0 && data[ 15 ] == 0 ) )
|
||||
{
|
||||
serial.append( format( data[ 15 ] ) );
|
||||
serial.append( format( data[ 14 ] ) );
|
||||
serial.append( format( data[ 13 ] ) );
|
||||
serial.append( format( data[ 12 ] ) );
|
||||
serial.append( "-" );
|
||||
}
|
||||
|
||||
serial.append( format( data[ 19 ] ) );
|
||||
serial.append( format( data[ 18 ] ) );
|
||||
serial.append( format( data[ 17 ] ) );
|
||||
serial.append( format( data[ 16 ] ) );
|
||||
serial.append( "-" );
|
||||
serial.append( format( data[ 23 ] ) );
|
||||
serial.append( format( data[ 22 ] ) );
|
||||
serial.append( format( data[ 21 ] ) );
|
||||
serial.append( format( data[ 20 ] ) );
|
||||
|
||||
mSerialNumber = serial.toString();
|
||||
return "Unknown";
|
||||
}
|
||||
else
|
||||
|
||||
String version = mFirmwareVersion;
|
||||
int lineBreak = version.indexOf('\n');
|
||||
|
||||
if(lineBreak >= 0)
|
||||
{
|
||||
mLog.error( "Error setting airspy part and serial byte data - null:" +
|
||||
( data == null ? " TRUE" : " FALSE length:" + data.length ) );
|
||||
version = version.substring(0, lineBreak);
|
||||
}
|
||||
|
||||
return version.replace("AirSpy NOS ", "").trim();
|
||||
}
|
||||
|
||||
private String format( byte value )
|
||||
{
|
||||
return String.format( "%02X", value );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Serial number, formatted as the identifier that tuner configurations are saved under.
|
||||
*/
|
||||
public String getSerialNumber()
|
||||
{
|
||||
return mSerialNumber;
|
||||
long serial = mPartIdSerialNumber.serialNumber();
|
||||
return String.format("%08X-%08X", serial >>> 32, serial & 0xFFFFFFFFL);
|
||||
}
|
||||
|
||||
|
||||
public String getPartNumber()
|
||||
{
|
||||
return mPartNumber;
|
||||
return String.format("%08X%08X", mPartIdSerialNumber.partId1(), mPartIdSerialNumber.partId2());
|
||||
}
|
||||
|
||||
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append( "Airspy Device Information\n" );
|
||||
sb.append( "Board: " );
|
||||
sb.append( getBoardID().getLabel() );
|
||||
sb.append( "\nPart Number: " );
|
||||
sb.append( getPartNumber() );
|
||||
sb.append( "\nSerial Number: " );
|
||||
sb.append( getSerialNumber() );
|
||||
sb.append( "\nFirmware: " );
|
||||
sb.append( getVersion() );
|
||||
|
||||
sb.append("Airspy Device Information\n");
|
||||
sb.append("Board: ");
|
||||
sb.append(getBoardName());
|
||||
sb.append("\nPart Number: ");
|
||||
sb.append(getPartNumber());
|
||||
sb.append("\nSerial Number: ");
|
||||
sb.append(getSerialNumber());
|
||||
sb.append("\nFirmware: ");
|
||||
sb.append(getVersion());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import io.github.dsheirer.preference.source.ChannelizerType;
|
||||
import io.github.dsheirer.source.tuner.ITunerErrorListener;
|
||||
import io.github.dsheirer.source.tuner.Tuner;
|
||||
import io.github.dsheirer.source.tuner.TunerClass;
|
||||
import io.github.dsheirer.source.tuner.airspy.AirspyTunerController.BoardID;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -71,7 +70,7 @@ public class AirspyTuner extends Tuner
|
||||
mLog.error("error getting serial number", e);
|
||||
}
|
||||
|
||||
return BoardID.AIRSPY.getLabel();
|
||||
return "Airspy";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -689,8 +689,8 @@ public class AirspyTunerEditor extends TunerEditor<AirspyTuner, AirspyTunerConfi
|
||||
sb.append(info.getPartNumber());
|
||||
sb.append("<br>");
|
||||
|
||||
sb.append("<b>Board ID: </b>");
|
||||
sb.append(info.getBoardID().getLabel());
|
||||
sb.append("<b>Board: </b>");
|
||||
sb.append(info.getBoardName());
|
||||
sb.append("<br>");
|
||||
|
||||
return sb.toString();
|
||||
|
||||
@@ -39,9 +39,6 @@ import io.github.dsheirer.vector.calibrate.magnitude.MagnitudeCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.mixer.ComplexMixerCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.oscillator.ComplexOscillatorCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.oscillator.RealOscillatorCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.sample.PackedSampleConverterCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.sample.UnpackedInterleavedSampleConverterCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.sample.UnpackedSampleConverterCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.sync.DMRSoftSyncCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.sync.NXDNSoftSyncCalibration;
|
||||
import io.github.dsheirer.vector.calibrate.sync.P25P1SoftSyncCalibration;
|
||||
@@ -103,9 +100,6 @@ public class CalibrationManager
|
||||
|
||||
sInstance = new CalibrationManager();
|
||||
|
||||
sInstance.add(new PackedSampleConverterCalibration());
|
||||
sInstance.add(new UnpackedSampleConverterCalibration());
|
||||
sInstance.add(new UnpackedInterleavedSampleConverterCalibration());
|
||||
sInstance.add(new AmDemodulatorCalibration());
|
||||
sInstance.add(new ChannelDFTCalibration());
|
||||
sInstance.add(new ComplexGainCalibration());
|
||||
|
||||
@@ -53,9 +53,6 @@ public enum CalibrationType
|
||||
OSCILLATOR_REAL("Real Oscillator", 1),
|
||||
P25P1_SOFT_SYNC_DETECTOR("P25 Phase 1 Soft Sync Detector", 2),
|
||||
P25P2_SOFT_SYNC_DETECTOR("P25 Phase 2 Soft Sync Detector", 1),
|
||||
SAMPLE_PACKED_CONVERTER("Packed Sample Converter", 2),
|
||||
SAMPLE_UNPACKED_INTERLEAVED_ITERATOR("Unpacked Interleaved Sample Iterator", 2),
|
||||
SAMPLE_UNPACKED_ITERATOR("Unpacked Sample Iterator", 2),
|
||||
SQUELCHING_FM_DEMODULATOR("Squelching FM Demodulator", 1),
|
||||
WINDOW("Window", 1);
|
||||
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2026 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.vector.calibrate.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.sample.ScalarUnpackedSampleConverter;
|
||||
import io.github.dsheirer.buffer.sample.VectorUnpackedSampleConverter;
|
||||
import io.github.dsheirer.vector.calibrate.Calibration;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationException;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import java.nio.ByteBuffer;
|
||||
import org.apache.commons.math3.stat.descriptive.moment.Mean;
|
||||
|
||||
public class PackedSampleConverterCalibration extends Calibration
|
||||
{
|
||||
private static final int BUFFER_SIZE = 262144;
|
||||
private static final int ITERATION_DURATION_MS = 1000;
|
||||
private static final int WARM_UP_ITERATIONS = 5;
|
||||
private static final int TEST_ITERATIONS = 5;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public PackedSampleConverterCalibration()
|
||||
{
|
||||
super(CalibrationType.SAMPLE_PACKED_CONVERTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calibrate() throws CalibrationException
|
||||
{
|
||||
Mean scalarMean = new Mean();
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
byte[] samples = new byte[BUFFER_SIZE];
|
||||
ByteBuffer buffer = ByteBuffer.wrap(samples);
|
||||
|
||||
for(int scalarTest = 0; scalarTest < WARM_UP_ITERATIONS; scalarTest++)
|
||||
{
|
||||
long score = testScalar(buffer);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("PACKED SAMPLE CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
for(int vectorTest = 0; vectorTest < WARM_UP_ITERATIONS; vectorTest++)
|
||||
{
|
||||
long score = testVector(buffer);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("PACKED SAMPLE CONVERTER WARMUP - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
|
||||
scalarMean.clear();
|
||||
vectorMean.clear();
|
||||
|
||||
for(int scalarTest = 0; scalarTest < TEST_ITERATIONS; scalarTest++)
|
||||
{
|
||||
long score = testScalar(buffer);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("PACKED SAMPLE CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
for(int vectorTest = 0; vectorTest < TEST_ITERATIONS; vectorTest++)
|
||||
{
|
||||
long score = testVector(buffer);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("PACKED SAMPLE CONVERTER - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
|
||||
if(scalarMean.getResult() > vectorMean.getResult())
|
||||
{
|
||||
setImplementation(Implementation.SCALAR);
|
||||
}
|
||||
else
|
||||
{
|
||||
setImplementation(Implementation.VECTOR_SIMD_PREFERRED);
|
||||
}
|
||||
|
||||
mLog.info("PACKED SAMPLE CONVERTER - SETTING OPTIMAL IMPLEMENTATION TO: " + getImplementation());
|
||||
}
|
||||
|
||||
private long testScalar(ByteBuffer buffer)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
ScalarUnpackedSampleConverter scalor = new ScalarUnpackedSampleConverter();
|
||||
|
||||
long count = 0;
|
||||
double accumulator = 0.0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
short[] converted = scalor.convert(buffer);
|
||||
accumulator += converted[2];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector(ByteBuffer buffer)
|
||||
{
|
||||
VectorUnpackedSampleConverter vector = new VectorUnpackedSampleConverter();
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
short[] converted = vector.convert(buffer);
|
||||
accumulator += converted[2];
|
||||
count++;
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2026 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.vector.calibrate.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIterator;
|
||||
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorScalar;
|
||||
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector128Bits;
|
||||
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector256Bits;
|
||||
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector512Bits;
|
||||
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector64Bits;
|
||||
import io.github.dsheirer.vector.calibrate.Calibration;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationException;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import org.apache.commons.math3.stat.descriptive.moment.Mean;
|
||||
|
||||
/**
|
||||
* Calculates optimal implementation (SCALAR vs VECTOR) for interleaved sample native buffers.
|
||||
*/
|
||||
public class UnpackedInterleavedSampleConverterCalibration extends Calibration
|
||||
{
|
||||
private static final int BUFFER_SIZE = 131072;
|
||||
private static final int ITERATION_DURATION_MS = 1000;
|
||||
private static final int WARMUP_ITERATIONS = 5;
|
||||
private static final int TEST_ITERATIONS = 5;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public UnpackedInterleavedSampleConverterCalibration()
|
||||
{
|
||||
super(CalibrationType.SAMPLE_UNPACKED_INTERLEAVED_ITERATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calibrate() throws CalibrationException
|
||||
{
|
||||
short[] samples = getShortSamples(BUFFER_SIZE);
|
||||
short[] residualI = getShortSamples(SampleBufferIterator.I_OVERLAP);
|
||||
short[] residualQ = getShortSamples(SampleBufferIterator.Q_OVERLAP);
|
||||
|
||||
//Warm-Up Phase ....
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR SIMD LANES PREFERRED: " + FloatVector.SPECIES_PREFERRED.length());
|
||||
|
||||
Mean scalarMean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateScalar(samples, residualI, residualQ);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
switch(FloatVector.SPECIES_PREFERRED.length())
|
||||
{
|
||||
//Deliberate fall-through of each case statement so that we can test the largest
|
||||
//SIMD lane width supported by hardware down to the smallest SIMD lane width.
|
||||
case 16:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector512(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector256(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector128(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < WARMUP_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector64(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
|
||||
//Test Phase ....
|
||||
scalarMean.clear();
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateScalar(samples, residualI, residualQ);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
double bestScore = scalarMean.getResult();
|
||||
setImplementation(Implementation.SCALAR);
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
switch(FloatVector.SPECIES_PREFERRED.length())
|
||||
{
|
||||
//Deliberate fall-through of each case statement so that we can test the largest
|
||||
//SIMD lane width supported by hardware down to the smallest SIMD lane width.
|
||||
case 16:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector512(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
if(vectorMean.getResult() > bestScore)
|
||||
{
|
||||
bestScore = vectorMean.getResult();
|
||||
setImplementation(Implementation.VECTOR_SIMD_512);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector256(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
if(vectorMean.getResult() > bestScore)
|
||||
{
|
||||
bestScore = vectorMean.getResult();
|
||||
setImplementation(Implementation.VECTOR_SIMD_256);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector128(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
if(vectorMean.getResult() > bestScore)
|
||||
{
|
||||
bestScore = vectorMean.getResult();
|
||||
setImplementation(Implementation.VECTOR_SIMD_128);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Mean vectorMean = new Mean();
|
||||
|
||||
for(int x = 0; x < TEST_ITERATIONS; x++)
|
||||
{
|
||||
long score = calibrateVector64(samples, residualI, residualQ);
|
||||
vectorMean.increment(score);
|
||||
}
|
||||
|
||||
if(vectorMean.getResult() > bestScore)
|
||||
{
|
||||
setImplementation(Implementation.VECTOR_SIMD_64);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - SET OPTIMAL IMPLEMENTATION TO: " + getImplementation());
|
||||
}
|
||||
|
||||
private long calibrateScalar(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleInterleavedBufferIteratorScalar iterator = new SampleInterleavedBufferIteratorScalar(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().samples()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long calibrateVector64(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleInterleavedBufferIteratorVector64Bits iterator = new SampleInterleavedBufferIteratorVector64Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().samples()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long calibrateVector128(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
for(int x = 0; x < ITERATION_DURATION_MS; x++)
|
||||
{
|
||||
SampleInterleavedBufferIteratorVector128Bits iterator = new SampleInterleavedBufferIteratorVector128Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().samples()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long calibrateVector256(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleInterleavedBufferIteratorVector256Bits iterator =
|
||||
new SampleInterleavedBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().samples()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long calibrateVector512(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleInterleavedBufferIteratorVector512Bits iterator =
|
||||
new SampleInterleavedBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
|
||||
System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().samples()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
}
|
||||
@@ -1,313 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2026 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.vector.calibrate.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIterator;
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIteratorScalar;
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector128Bits;
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector256Bits;
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector512Bits;
|
||||
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector64Bits;
|
||||
import io.github.dsheirer.vector.calibrate.Calibration;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationException;
|
||||
import io.github.dsheirer.vector.calibrate.CalibrationType;
|
||||
import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
import org.apache.commons.math3.stat.descriptive.moment.Mean;
|
||||
|
||||
/**
|
||||
* Calculates optimal implementation (SCALAR vs VECTOR) for non-interleaved sample native buffers.
|
||||
*/
|
||||
public class UnpackedSampleConverterCalibration extends Calibration
|
||||
{
|
||||
private static final int BUFFER_SIZE = 131072;
|
||||
private static final int ITERATION_DURATION_MS = 1000;
|
||||
private static final int WARMUP_ITERATIONS = 5;
|
||||
private static final int TEST_ITERATIONS = 5;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public UnpackedSampleConverterCalibration()
|
||||
{
|
||||
super(CalibrationType.SAMPLE_UNPACKED_ITERATOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void calibrate() throws CalibrationException
|
||||
{
|
||||
short[] samples = getShortSamples(BUFFER_SIZE);
|
||||
short[] residualI = getShortSamples(SampleBufferIterator.I_OVERLAP);
|
||||
short[] residualQ = getShortSamples(SampleBufferIterator.Q_OVERLAP);
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR SIMD LANES PREFERRED: " + FloatVector.SPECIES_PREFERRED.length());
|
||||
|
||||
//Warm-Up Phase ....
|
||||
Mean scalarMean = new Mean();
|
||||
|
||||
for(int warmup = 0; warmup < WARMUP_ITERATIONS; warmup++)
|
||||
{
|
||||
long score = testScalar(samples, residualI, residualQ);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
switch(FloatVector.SPECIES_PREFERRED.length())
|
||||
{
|
||||
//Deliberate fall-through of each case statement so that we can test the largest
|
||||
//SIMD lane width supported by hardware down to the smallest SIMD lane width.
|
||||
case 16:
|
||||
{
|
||||
Mean vector512Mean = new Mean();
|
||||
for(int warmup = 0; warmup < WARMUP_ITERATIONS; warmup++)
|
||||
{
|
||||
long score = testVector512(samples, residualI, residualQ);
|
||||
vector512Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Mean vector256Mean = new Mean();
|
||||
for(int warmup = 0; warmup < WARMUP_ITERATIONS; warmup++)
|
||||
{
|
||||
long score = testVector256(samples, residualI, residualQ);
|
||||
vector256Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
Mean vector128Mean = new Mean();
|
||||
for(int warmup = 0; warmup < WARMUP_ITERATIONS; warmup++)
|
||||
{
|
||||
long score = testVector128(samples, residualI, residualQ);
|
||||
vector128Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Mean vector64Mean = new Mean();
|
||||
for(int warmup = 0; warmup < WARMUP_ITERATIONS; warmup++)
|
||||
{
|
||||
long score = testVector64(samples, residualI, residualQ);
|
||||
vector64Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
|
||||
}
|
||||
}
|
||||
|
||||
//Test Phase ....
|
||||
scalarMean.clear();
|
||||
|
||||
for(int test = 0; test < TEST_ITERATIONS; test++)
|
||||
{
|
||||
long score = testScalar(samples, residualI, residualQ);
|
||||
scalarMean.increment(score);
|
||||
}
|
||||
|
||||
double bestScore = scalarMean.getResult();
|
||||
setImplementation(Implementation.SCALAR);
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
|
||||
|
||||
switch(FloatVector.SPECIES_PREFERRED.length())
|
||||
{
|
||||
//Deliberate fall-through of each case statement so that we can test the largest
|
||||
//SIMD lane width supported by hardware down to the smallest SIMD lane width.
|
||||
case 16:
|
||||
{
|
||||
Mean vector512Mean = new Mean();
|
||||
for(int test = 0; test < TEST_ITERATIONS; test++)
|
||||
{
|
||||
long score = testVector512(samples, residualI, residualQ);
|
||||
vector512Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
|
||||
if(vector512Mean.getResult() > bestScore)
|
||||
{
|
||||
bestScore = vector512Mean.getResult();
|
||||
setImplementation(Implementation.VECTOR_SIMD_512);
|
||||
}
|
||||
}
|
||||
case 8:
|
||||
{
|
||||
Mean vector256Mean = new Mean();
|
||||
for(int test = 0; test < TEST_ITERATIONS; test++)
|
||||
{
|
||||
long score = testVector256(samples, residualI, residualQ);
|
||||
vector256Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
|
||||
if(vector256Mean.getResult() > bestScore)
|
||||
{
|
||||
bestScore = vector256Mean.getResult();
|
||||
setImplementation(Implementation.VECTOR_SIMD_256);
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
{
|
||||
Mean vector128Mean = new Mean();
|
||||
for(int test = 0; test < TEST_ITERATIONS; test++)
|
||||
{
|
||||
long score = testVector128(samples, residualI, residualQ);
|
||||
vector128Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
|
||||
if(vector128Mean.getResult() > bestScore)
|
||||
{
|
||||
bestScore = vector128Mean.getResult();
|
||||
setImplementation(Implementation.VECTOR_SIMD_128);
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Mean vector64Mean = new Mean();
|
||||
for(int test = 0; test < TEST_ITERATIONS; test++)
|
||||
{
|
||||
long score = testVector64(samples, residualI, residualQ);
|
||||
vector64Mean.increment(score);
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
|
||||
if(vector64Mean.getResult() > bestScore)
|
||||
{
|
||||
setImplementation(Implementation.VECTOR_SIMD_64);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mLog.info("UNPACKED SAMPLE CONVERTER - SET OPTIMAL IMPLEMENTATION TO: " + getImplementation());
|
||||
}
|
||||
|
||||
private long testScalar(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleBufferIteratorScalar iterator = new SampleBufferIteratorScalar(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().i()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector64(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleBufferIteratorVector64Bits iterator = new SampleBufferIteratorVector64Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().i()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector128(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleBufferIteratorVector128Bits iterator = new SampleBufferIteratorVector128Bits(samples, residualI,
|
||||
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().i()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector256(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleBufferIteratorVector256Bits iterator =
|
||||
new SampleBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f,
|
||||
System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().i()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
|
||||
private long testVector512(short[] samples, short[] residualI, short[] residualQ)
|
||||
{
|
||||
long start = System.currentTimeMillis();
|
||||
double accumulator = 0.0;
|
||||
long count = 0;
|
||||
|
||||
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
|
||||
{
|
||||
SampleBufferIteratorVector512Bits iterator =
|
||||
new SampleBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
|
||||
System.currentTimeMillis(), 0.0f);
|
||||
|
||||
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
|
||||
{
|
||||
accumulator += iterator.next().i()[2];
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count + (long)(accumulator * 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user