mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Hydrasdr Tuner Support (#2339)
HydraSDR tuner support * Adds Bias-T option * Update nightly.yml * Fix BoardID * Consolidated calibrations and sample converters and removed logging. --------- Co-authored-by: Benjamin Vernoux <bvernoux@gmail.com> Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy.hf;
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy.hf;
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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.hydrasdr;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.hilbert.HilbertTransform;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Base complex samples iterator for raw HydraSDR 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 HydraSdrBufferIterator<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 HydraSDR.
|
||||
* @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 HydraSdrBufferIterator(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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Scalar implementation for non-packed HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrBufferIteratorScalar extends HydraSdrBufferIterator<ComplexSamples>
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrBufferIteratorScalar(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrBufferIteratorVector128Bits extends HydraSdrBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrBufferIteratorVector128Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrBufferIteratorVector256Bits extends HydraSdrBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrBufferIteratorVector256Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrBufferIteratorVector512Bits extends HydraSdrBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float[] mFilterPart2;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrBufferIteratorVector512Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrBufferIteratorVector64Bits extends HydraSdrBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrBufferIteratorVector64Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
/**
|
||||
* Scalar implementation for non-packed HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrInterleavedBufferIteratorScalar extends HydraSdrBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrInterleavedBufferIteratorScalar(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrInterleavedBufferIteratorVector128Bits extends HydraSdrBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrInterleavedBufferIteratorVector128Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrInterleavedBufferIteratorVector256Bits extends HydraSdrBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrInterleavedBufferIteratorVector256Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrInterleavedBufferIteratorVector512Bits extends HydraSdrBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float[] mFilterPart2;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrInterleavedBufferIteratorVector512Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
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 HydraSDR native buffers
|
||||
*/
|
||||
public class HydraSdrInterleavedBufferIteratorVector64Bits extends HydraSdrBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the HydraSDR, 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 HydraSdrInterleavedBufferIteratorVector64Bits(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);
|
||||
}
|
||||
}
|
||||
@@ -17,18 +17,18 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Interface to convert airspy samples from a byte buffer to an array of shorts.
|
||||
* Interface to convert HydraSDR samples from a byte buffer to an array of shorts.
|
||||
*/
|
||||
public interface IAirspySampleConverter
|
||||
public interface IHydraSdrSampleConverter
|
||||
{
|
||||
/**
|
||||
* Converts the airspy byte samples contained in the byte buffer to their short-valued representation
|
||||
* @param buffer of airspy samples
|
||||
* Converts the HydraSDR byte samples contained in the byte buffer to their short-valued representation
|
||||
* @param buffer of HydraSDR samples
|
||||
* @return converted samples
|
||||
*/
|
||||
short[] convert(ByteBuffer buffer);
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Scalar implementation of HydraSDR sample converter for packed samples.
|
||||
*/
|
||||
public class ScalarPackedSampleConverter implements IHydraSdrSampleConverter
|
||||
{
|
||||
/**
|
||||
* 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 *= HydraSdrBufferIterator.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();
|
||||
}
|
||||
}
|
||||
@@ -17,15 +17,15 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Scalar implementation of airspy sample converter for un-packed samples.
|
||||
* Scalar implementation of HydraSDR sample converter for un-packed samples.
|
||||
*/
|
||||
public class ScalarUnpackedSampleConverter implements IAirspySampleConverter
|
||||
public class ScalarUnpackedSampleConverter implements IHydraSdrSampleConverter
|
||||
{
|
||||
/**
|
||||
* Manages DC calculations and processing interval
|
||||
@@ -58,7 +58,7 @@ public class ScalarUnpackedSampleConverter implements IAirspySampleConverter
|
||||
}
|
||||
|
||||
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
|
||||
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
averageDcNow *= HydraSdrBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
mDcCalculationManager.adjust(averageDcNow);
|
||||
}
|
||||
else
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.hydrasdr;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
@@ -26,9 +26,9 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Scalar implementation of airspy sample converter for un-packed samples.
|
||||
* Vector implementation of HydraSDR sample converter for un-packed samples.
|
||||
*/
|
||||
public class VectorUnpackedSampleConverter implements IAirspySampleConverter
|
||||
public class VectorUnpackedSampleConverter implements IHydraSdrSampleConverter
|
||||
{
|
||||
private static final VectorSpecies<Short> VECTOR_SPECIES = ShortVector.SPECIES_PREFERRED;
|
||||
|
||||
@@ -88,7 +88,7 @@ public class VectorUnpackedSampleConverter implements IAirspySampleConverter
|
||||
}
|
||||
|
||||
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
|
||||
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
averageDcNow *= HydraSdrBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
mDcCalculationManager.adjust(averageDcNow);
|
||||
}
|
||||
else
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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();
|
||||
}
|
||||
@@ -17,17 +17,17 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.dsp.filter.hilbert.HilbertTransform;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Base complex samples iterator for raw Airspy sample buffers. Incorporates a Hilbert transform filter
|
||||
* 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 AirspyBufferIterator<T> implements Iterator<T>
|
||||
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;
|
||||
@@ -47,14 +47,14 @@ public abstract class AirspyBufferIterator<T> implements Iterator<T>
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param samples from the airspy.
|
||||
* @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 AirspyBufferIterator(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp,
|
||||
public SampleBufferIterator(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp,
|
||||
float samplesPerMillisecond)
|
||||
{
|
||||
if(residualI.length != I_OVERLAP || residualQ.length != Q_OVERLAP)
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,27 +17,27 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Scalar implementation for non-packed Airspy native buffers
|
||||
* Scalar implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyBufferIteratorScalar extends AirspyBufferIterator<ComplexSamples>
|
||||
public class SampleBufferIteratorScalar extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp, float samplesPerMillisecond)
|
||||
public SampleBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc, long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
@@ -26,23 +26,23 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyBufferIteratorVector128Bits extends AirspyBufferIterator<ComplexSamples>
|
||||
public class SampleBufferIteratorVector128Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
@@ -26,23 +26,23 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyBufferIteratorVector256Bits extends AirspyBufferIterator<ComplexSamples>
|
||||
public class SampleBufferIteratorVector256Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
@@ -26,9 +26,9 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyBufferIteratorVector512Bits extends AirspyBufferIterator<ComplexSamples>
|
||||
public class SampleBufferIteratorVector512Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float[] mFilterPart2;
|
||||
@@ -36,14 +36,14 @@ public class AirspyBufferIteratorVector512Bits extends AirspyBufferIterator<Comp
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import java.util.Arrays;
|
||||
@@ -26,23 +26,23 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyBufferIteratorVector64Bits extends AirspyBufferIterator<ComplexSamples>
|
||||
public class SampleBufferIteratorVector64Bits extends SampleBufferIterator<ComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,26 +17,26 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
|
||||
/**
|
||||
* Scalar implementation for non-packed Airspy native buffers
|
||||
* Scalar implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyInterleavedBufferIteratorScalar extends AirspyBufferIterator<InterleavedComplexSamples>
|
||||
public class SampleInterleavedBufferIteratorScalar extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyInterleavedBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleInterleavedBufferIteratorScalar(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
@@ -25,23 +25,23 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyInterleavedBufferIteratorVector128Bits extends AirspyBufferIterator<InterleavedComplexSamples>
|
||||
public class SampleInterleavedBufferIteratorVector128Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_128;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyInterleavedBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleInterleavedBufferIteratorVector128Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
@@ -25,23 +25,23 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyInterleavedBufferIteratorVector256Bits extends AirspyBufferIterator<InterleavedComplexSamples>
|
||||
public class SampleInterleavedBufferIteratorVector256Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_256;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyInterleavedBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleInterleavedBufferIteratorVector256Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
@@ -25,9 +25,9 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyInterleavedBufferIteratorVector512Bits extends AirspyBufferIterator<InterleavedComplexSamples>
|
||||
public class SampleInterleavedBufferIteratorVector512Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_512;
|
||||
private float[] mFilterPart2;
|
||||
@@ -35,14 +35,14 @@ public class AirspyInterleavedBufferIteratorVector512Bits extends AirspyBufferIt
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyInterleavedBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleInterleavedBufferIteratorVector512Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import jdk.incubator.vector.FloatVector;
|
||||
@@ -25,23 +25,23 @@ import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
/**
|
||||
* Vector SIMD implementation for non-packed Airspy native buffers
|
||||
* Vector SIMD implementation for non-packed native buffers
|
||||
*/
|
||||
public class AirspyInterleavedBufferIteratorVector64Bits extends AirspyBufferIterator<InterleavedComplexSamples>
|
||||
public class SampleInterleavedBufferIteratorVector64Bits extends SampleBufferIterator<InterleavedComplexSamples>
|
||||
{
|
||||
private static final VectorSpecies<Float> VECTOR_SPECIES = FloatVector.SPECIES_64;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param samples from the airspy, either packed or unpacked.
|
||||
* @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 AirspyInterleavedBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
public SampleInterleavedBufferIteratorVector64Bits(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
long timestamp, float samplesPerMillisecond)
|
||||
{
|
||||
super(samples, residualI, residualQ, averageDc, timestamp, samplesPerMillisecond);
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
@@ -26,9 +26,9 @@ import io.github.dsheirer.vector.calibrate.Implementation;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Native buffer scalar implementation for Airspy non-packed samples.
|
||||
* Native buffer scalar implementation for non-packed samples.
|
||||
*/
|
||||
public class AirspyNativeBuffer extends AbstractNativeBuffer
|
||||
public class SampleNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
private short[] mSamples;
|
||||
private short[] mResidualI;
|
||||
@@ -39,7 +39,7 @@ public class AirspyNativeBuffer extends AbstractNativeBuffer
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param samples (non-packed) from the airspy device
|
||||
* @param samples (non-packed) from the device
|
||||
* @param residualI samples from previous buffer
|
||||
* @param residualQ samples from previous buffer
|
||||
* @param averageDc measured
|
||||
@@ -48,17 +48,17 @@ public class AirspyNativeBuffer extends AbstractNativeBuffer
|
||||
* @param nonInterleavedImplementation optimal, scalar vs vector SIMD
|
||||
* @param samplesPerMillisecond used to calculate sub-buffer fragment timestamp offsets from the start of this buffer.
|
||||
*/
|
||||
public AirspyNativeBuffer(short[] samples, short[] residualI, short[] residualQ, float averageDc,
|
||||
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 % AirspyBufferIterator.FRAGMENT_SIZE != 0)
|
||||
if(samples.length % SampleBufferIterator.FRAGMENT_SIZE != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Samples short[] length [" + samples.length +
|
||||
"] must be an even multiple of " + AirspyBufferIterator.FRAGMENT_SIZE);
|
||||
"] must be an even multiple of " + SampleBufferIterator.FRAGMENT_SIZE);
|
||||
}
|
||||
|
||||
mSamples = samples;
|
||||
@@ -80,11 +80,11 @@ public class AirspyNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
return switch(mInterleavedImplementation)
|
||||
{
|
||||
case VECTOR_SIMD_512 -> new AirspyBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_256-> new AirspyBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_128-> new AirspyBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_64 ->new AirspyBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
default -> new AirspyBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
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());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,11 +93,11 @@ public class AirspyNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
return switch(mInterleavedImplementation)
|
||||
{
|
||||
case VECTOR_SIMD_512 -> new AirspyInterleavedBufferIteratorVector512Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_256-> new AirspyInterleavedBufferIteratorVector256Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_128-> new AirspyInterleavedBufferIteratorVector128Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
case VECTOR_SIMD_64 ->new AirspyInterleavedBufferIteratorVector64Bits(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
default -> new AirspyInterleavedBufferIteratorScalar(mSamples, mResidualI, mResidualQ, mAverageDc, getTimestamp(), getSamplesPerMillisecond());
|
||||
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,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
@@ -30,26 +30,26 @@ import java.util.Arrays;
|
||||
/**
|
||||
* Implements a factory for creating SignedByteNativeBuffer instances
|
||||
*/
|
||||
public class AirspyNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
public class SampleNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
private boolean mSamplePacking = false;
|
||||
private short[] mResidualI = new short[AirspyBufferIterator.I_OVERLAP];
|
||||
private short[] mResidualQ = new short[AirspyBufferIterator.Q_OVERLAP];
|
||||
private IAirspySampleConverter mConverter;
|
||||
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 AirspyNativeBufferFactory()
|
||||
public SampleNativeBufferFactory()
|
||||
{
|
||||
updateConverter();
|
||||
|
||||
mInterleavedIteratorImplementation = CalibrationManager.getInstance()
|
||||
.getImplementation(CalibrationType.AIRSPY_UNPACKED_INTERLEAVED_ITERATOR);
|
||||
.getImplementation(CalibrationType.SAMPLE_UNPACKED_INTERLEAVED_ITERATOR);
|
||||
mNonInterleavedIteratorImplementation = CalibrationManager.getInstance()
|
||||
.getImplementation(CalibrationType.AIRSPY_UNPACKED_ITERATOR);
|
||||
.getImplementation(CalibrationType.SAMPLE_UNPACKED_ITERATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,7 +78,7 @@ public class AirspyNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
}
|
||||
else
|
||||
{
|
||||
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.AIRSPY_SAMPLE_CONVERTER);
|
||||
Implementation implementation = CalibrationManager.getInstance().getImplementation(CalibrationType.SAMPLE_PACKED_CONVERTER);
|
||||
|
||||
if(implementation == Implementation.VECTOR_SIMD_PREFERRED)
|
||||
{
|
||||
@@ -102,7 +102,7 @@ public class AirspyNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
short[] samples = mConverter.convert(buffer);
|
||||
|
||||
INativeBuffer nativeBuffer = new AirspyNativeBuffer(samples,
|
||||
INativeBuffer nativeBuffer = new SampleNativeBuffer(samples,
|
||||
Arrays.copyOf(mResidualI, mResidualI.length),
|
||||
Arrays.copyOf(mResidualQ, mResidualQ.length), mConverter.getAverageDc(), timestamp,
|
||||
mInterleavedIteratorImplementation, mNonInterleavedIteratorImplementation, getSamplesPerMillisecond());
|
||||
@@ -118,16 +118,16 @@ public class AirspyNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
*/
|
||||
private void extractResidual(short[] samples)
|
||||
{
|
||||
int offset = samples.length - (AirspyBufferIterator.I_OVERLAP * 2);
|
||||
int offset = samples.length - (SampleBufferIterator.I_OVERLAP * 2);
|
||||
|
||||
for(int i = 0; i < AirspyBufferIterator.I_OVERLAP; i++)
|
||||
for(int i = 0; i < SampleBufferIterator.I_OVERLAP; i++)
|
||||
{
|
||||
mResidualI[i] = samples[offset + (2 * i)];
|
||||
}
|
||||
|
||||
offset = samples.length - (AirspyBufferIterator.Q_OVERLAP * 2) + 1;
|
||||
offset = samples.length - (SampleBufferIterator.Q_OVERLAP * 2) + 1;
|
||||
|
||||
for(int q = 0; q < AirspyBufferIterator.Q_OVERLAP; q++)
|
||||
for(int q = 0; q < SampleBufferIterator.Q_OVERLAP; q++)
|
||||
{
|
||||
mResidualQ[q] = samples[offset + (2 * q)];
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -17,15 +17,15 @@
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
package io.github.dsheirer.buffer.sample;
|
||||
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* Scalar implementation of airspy sample converter for un-packed samples.
|
||||
* Scalar implementation of sample converter for un-packed samples.
|
||||
*/
|
||||
public class ScalarPackedSampleConverter implements IAirspySampleConverter
|
||||
public class ScalarPackedSampleConverter implements ISampleConverter
|
||||
{
|
||||
/**
|
||||
* Manages DC calculations and processing interval
|
||||
@@ -64,7 +64,7 @@ public class ScalarPackedSampleConverter implements IAirspySampleConverter
|
||||
}
|
||||
|
||||
float averageDcNow = ((float)dcAccumulator / (float)samples.length) - 2048.0f;
|
||||
averageDcNow *= AirspyBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
averageDcNow *= SampleBufferIterator.SCALE_SIGNED_12_BIT_TO_FLOAT;
|
||||
mDcCalculationManager.adjust(averageDcNow);
|
||||
}
|
||||
else
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user