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:
Denny Sheirer
2025-12-25 04:18:12 -05:00
committed by GitHub
parent b0a0ad4b43
commit d8e2e0ac57
53 changed files with 4153 additions and 221 deletions

View File

@@ -37,11 +37,12 @@ jobs:
echo -e "ref: $GITHUB_REF \ncommit: $GITHUB_SHA\nbuild: $(date +"%Y-%m-%dT%H:%M:%SZ")" > build/image/build_info.txt
- name: Add build artifacts to nightly release
uses: pyTooling/Actions/releaser@main
uses: softprops/action-gh-release@v2
with:
tag: nightly
rm: false
token: ${{ secrets.GITHUB_TOKEN }}
tag_name: nightly
prerelease: true
files: |
build/image/*.zip
build/image/build_info.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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();
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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)

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);

View File

@@ -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());
};
}
}

View File

@@ -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)];
}

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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();
}
}

View File

@@ -30,6 +30,7 @@ public enum TunerClass
FUNCUBE_DONGLE_PRO("Funcube Dongle Pro" ),
FUNCUBE_DONGLE_PRO_PLUS("Funcube Dongle Pro+" ),
HACKRF("HackRF" ),
HYDRASDR("HydraSDR"),
RTL2832("RTL-2832"),
RSP("RSP"),
TEST_TUNER("Test"),
@@ -53,8 +54,8 @@ public enum TunerClass
return mDescription;
}
public static final EnumSet<TunerClass> SUPPORTED_USB_TUNERS = EnumSet.of(AIRSPY, AIRSPY_HF, HACKRF, RTL2832,
FUNCUBE_DONGLE_PRO, FUNCUBE_DONGLE_PRO_PLUS);
public static final EnumSet<TunerClass> SUPPORTED_USB_TUNERS = EnumSet.of(AIRSPY, AIRSPY_HF, HACKRF, HYDRASDR,
RTL2832, FUNCUBE_DONGLE_PRO, FUNCUBE_DONGLE_PRO_PLUS);
public static final EnumSet<TunerClass> FUNCUBE_TUNERS = EnumSet.of(FUNCUBE_DONGLE_PRO, FUNCUBE_DONGLE_PRO_PLUS);
@@ -121,6 +122,8 @@ public enum TunerClass
case 0x1D50CC15: //HACKRF ONE
case 0x1D50604B: //HACKRF JAWBREAKER
return HACKRF;
case 0x38AF0001: //HYDRASDR RFONE (official VID 38af, PID 0001)
return HYDRASDR;
case 0x1D5060A1:
return AIRSPY;
case 0x03EB800C:

View File

@@ -44,6 +44,10 @@ import io.github.dsheirer.source.tuner.hackrf.HackRFTuner;
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration;
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerController;
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerEditor;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTuner;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerConfiguration;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerController;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerEditor;
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
@@ -375,6 +379,8 @@ public class TunerFactory
throw new SourceException("Unable to find matching tuner sound card mixer");
case HACKRF:
return new HackRFTuner(new HackRFTunerController(bus, portAddress, tunerErrorListener), tunerErrorListener, channelizerType);
case HYDRASDR:
return new HydraSdrTuner(new HydraSdrTunerController(bus, portAddress, tunerErrorListener), tunerErrorListener, channelizerType);
case RTL2832:
return new RTL2832Tuner(new RTL2832TunerController(bus, portAddress, tunerErrorListener), tunerErrorListener, channelizerType);
default:
@@ -424,6 +430,8 @@ public class TunerFactory
case HACKRF_ONE:
case HACKRF_RAD1O:
return new HackRFTunerConfiguration(uniqueID);
case HYDRASDR_R828D:
return new HydraSdrTunerConfiguration(uniqueID);
case RAFAELMICRO_R820T:
return new R820TTunerConfiguration(uniqueID);
case RAFAELMICRO_R828D:
@@ -467,6 +475,8 @@ public class TunerFactory
return new FCD2TunerEditor(userPreferences, tunerManager, discoveredTuner);
case HACKRF:
return new HackRFTunerEditor(userPreferences, tunerManager, discoveredTuner);
case HYDRASDR:
return new HydraSdrTunerEditor(userPreferences, tunerManager, discoveredTuner);
case RSP:
if(discoveredTuner instanceof DiscoveredRspTuner discoveredRspTuner)
{

View File

@@ -36,6 +36,7 @@ public enum TunerType
HACKRF_ONE("ONE"),
HACKRF_JAWBREAKER("Jawbreaker"),
HACKRF_RAD1O("RAD1O"),
HYDRASDR_R828D("R828D"),
RAFAELMICRO_R820T("R820T"),
RAFAELMICRO_R828D("R828D"),
RSP_1("RSP1"),
@@ -73,7 +74,7 @@ public enum TunerType
* Supported USB tuner types
*/
public static EnumSet<TunerType> SUPPORTED_USB_TUNERS = EnumSet.of(AIRSPY_R820T, ELONICS_E4000, FITIPOWER_FC0012,
HACKRF_JAWBREAKER, HACKRF_RAD1O, HACKRF_ONE, RAFAELMICRO_R820T);
HACKRF_JAWBREAKER, HACKRF_RAD1O, HACKRF_ONE, HYDRASDR_R828D, RAFAELMICRO_R820T);
/**
* Supported sound card tuner types

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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
@@ -20,7 +20,7 @@
package io.github.dsheirer.source.tuner.airspy;
import io.github.dsheirer.buffer.INativeBufferFactory;
import io.github.dsheirer.buffer.airspy.AirspyNativeBufferFactory;
import io.github.dsheirer.buffer.sample.SampleNativeBufferFactory;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.TunerType;
@@ -69,7 +69,7 @@ public class AirspyTunerController extends USBTunerController
private static final byte USB_REQUEST_OUT = (byte) (LibUsb.ENDPOINT_OUT | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE);
public static final DecimalFormat MHZ_FORMATTER = new DecimalFormat("#.00 MHz");
public static final AirspySampleRate DEFAULT_SAMPLE_RATE = new AirspySampleRate(0, 10000000, "10.00 MHz");
private AirspyNativeBufferFactory mNativeBufferFactory = new AirspyNativeBufferFactory();
private SampleNativeBufferFactory mNativeBufferFactory = new SampleNativeBufferFactory();
private AirspyDeviceInformation mDeviceInfo;
private List<AirspySampleRate> mSampleRates = new ArrayList<>();
private int mSampleRate = 0;

View File

@@ -1,6 +1,6 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 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
@@ -20,7 +20,7 @@
package io.github.dsheirer.source.tuner.airspy.hf;
import io.github.dsheirer.buffer.INativeBufferFactory;
import io.github.dsheirer.buffer.airspy.hf.AirspyHfNativeBufferFactory;
import io.github.dsheirer.buffer.airspy.AirspyHfNativeBufferFactory;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.TunerType;

View File

@@ -29,6 +29,7 @@ import io.github.dsheirer.source.tuner.airspy.hf.AirspyHfTunerConfiguration;
import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerConfiguration;
import io.github.dsheirer.source.tuner.fcd.proplusV2.FCD2TunerConfiguration;
import io.github.dsheirer.source.tuner.hackrf.HackRFTunerConfiguration;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerConfiguration;
import io.github.dsheirer.source.tuner.recording.RecordingTunerConfiguration;
import io.github.dsheirer.source.tuner.rtl.e4k.E4KTunerConfiguration;
import io.github.dsheirer.source.tuner.rtl.fc0013.FC0013TunerConfiguration;
@@ -48,6 +49,7 @@ import io.github.dsheirer.source.tuner.sdrplay.RspTunerConfiguration;
@JsonSubTypes.Type(value = FCD1TunerConfiguration.class, name = "fcd1TunerConfiguration"),
@JsonSubTypes.Type(value = FCD2TunerConfiguration.class, name = "fcd2TunerConfiguration"),
@JsonSubTypes.Type(value = HackRFTunerConfiguration.class, name = "hackRFTunerConfiguration"),
@JsonSubTypes.Type(value = HydraSdrTunerConfiguration.class, name = "hydraSdrTunerConfiguration"),
@JsonSubTypes.Type(value = RecordingTunerConfiguration.class, name = "recordingTunerConfiguration"),
@JsonSubTypes.Type(value = R820TTunerConfiguration.class, name = "r820TTunerConfiguration"),
@JsonSubTypes.Type(value = R828DTunerConfiguration.class, name = "r828DTunerConfiguration"),

View File

@@ -0,0 +1,157 @@
package io.github.dsheirer.source.tuner.hydrasdr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
public class HydraSdrDeviceInformation
{
private final static Logger mLog =
LoggerFactory.getLogger( HydraSdrDeviceInformation.class );
private HydraSdrTunerController.BoardID mBoardID = HydraSdrTunerController.BoardID.UNKNOWN;
private String mFirmwareVersion = "Unknown";
private String mPartNumber = "Unknown";
private String mSerialNumber = "Unknown";
/**
* HydraSDR Device Information - provides parsing and access to device
* information components.
*/
public HydraSdrDeviceInformation()
{
}
public void setBoardID( int boardID )
{
mBoardID = HydraSdrTunerController.BoardID.fromValue( boardID );
}
public HydraSdrTunerController.BoardID getBoardID()
{
return mBoardID;
}
public void setVersion( byte[] data )
{
if( data != null && data.length > 0 )
{
/* Truncate the version string if we have a line break - ASCII 10 */
for( int x = 0; x < data.length; x++ )
{
if( data[ x ] == (byte)10 )
{
mFirmwareVersion = new String( Arrays.copyOf( data, x ) );
mFirmwareVersion = mFirmwareVersion.replace( "HydraSDR ", "" );
return;
}
}
mFirmwareVersion = new String( data );
mFirmwareVersion = mFirmwareVersion.replace( "HydraSDR ", "" );
}
else
{
mLog.error( "Error setting HydraSDR version byte data - null:" +
( data == null ? " TRUE" : " FALSE length:" + data.length ) );
}
}
public String getVersion()
{
return mFirmwareVersion;
}
public void setPartAndSerialNumber( byte[] data )
{
if( data != null && data.length == 24 )
{
//Note: values are byte-reversed (big-endian) 32-bit chunks
StringBuilder part = new StringBuilder();
part.append( format( data[ 3 ] ) );
part.append( format( data[ 2 ] ) );
part.append( format( data[ 1 ] ) );
part.append( format( data[ 0 ] ) );
part.append( format( data[ 7 ] ) );
part.append( format( data[ 6 ] ) );
part.append( format( data[ 5 ] ) );
part.append( format( data[ 4 ] ) );
mPartNumber = part.toString();
StringBuilder serial = new StringBuilder();
//Note: current library only exposes 64-bits of serial number
//address space, but appears to have room for 128-bits. We won't
//add the first 64 bits if it's zeros
if( !( data[ 8 ] == 0 && data[ 9 ] == 0 && data[ 10 ] == 0 && data[ 11 ] == 0 ) )
{
serial.append( format( data[ 11 ] ) );
serial.append( format( data[ 10 ] ) );
serial.append( format( data[ 9 ] ) );
serial.append( format( data[ 8 ] ) );
serial.append( "-" );
}
if( !( data[ 12 ] == 0 && data[ 13 ] == 0 && data[ 14 ] == 0 && data[ 15 ] == 0 ) )
{
serial.append( format( data[ 15 ] ) );
serial.append( format( data[ 14 ] ) );
serial.append( format( data[ 13 ] ) );
serial.append( format( data[ 12 ] ) );
serial.append( "-" );
}
serial.append( format( data[ 19 ] ) );
serial.append( format( data[ 18 ] ) );
serial.append( format( data[ 17 ] ) );
serial.append( format( data[ 16 ] ) );
serial.append( "-" );
serial.append( format( data[ 23 ] ) );
serial.append( format( data[ 22 ] ) );
serial.append( format( data[ 21 ] ) );
serial.append( format( data[ 20 ] ) );
mSerialNumber = serial.toString();
}
else
{
mLog.error( "Error setting HydraSDR part and serial byte data - null:" +
( data == null ? " TRUE" : " FALSE length:" + data.length ) );
}
}
private String format( byte value )
{
return String.format( "%02X", value );
}
public String getSerialNumber()
{
return mSerialNumber;
}
public String getPartNumber()
{
return mPartNumber;
}
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append( "HydraSDR Device Information\n" );
sb.append( "Board: " );
sb.append( getBoardID().getLabel() );
sb.append( "\nPart Number: " );
sb.append( getPartNumber() );
sb.append( "\nSerial Number: " );
sb.append( getSerialNumber() );
sb.append( "\nFirmware: " );
sb.append( getVersion() );
return sb.toString();
}
}

View File

@@ -0,0 +1,35 @@
package io.github.dsheirer.source.tuner.hydrasdr;
public class HydraSdrSampleRate
{
private int mIndex;
private int mValue;
private String mLabel;
public HydraSdrSampleRate( int index, int value, String label )
{
mIndex = index;
mValue = value;
mLabel = label;
}
public int getIndex()
{
return mIndex;
}
public int getRate()
{
return mValue;
}
public String getLabel()
{
return mLabel;
}
public String toString()
{
return getLabel();
}
}

View File

@@ -0,0 +1,95 @@
/*
* *****************************************************************************
* 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.source.tuner.hydrasdr;
import io.github.dsheirer.preference.source.ChannelizerType;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerClass;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerController.BoardID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* HydraSDR Tuner
*/
public class HydraSdrTuner extends Tuner
{
private final static Logger mLog = LoggerFactory.getLogger(HydraSdrTuner.class);
/**
* Constructs an instance
* @param controller for the HydraSDR
* @param tunerErrorListener to listen for errors from this tuner
* @param channelizerType for the channelizer
*/
public HydraSdrTuner(HydraSdrTunerController controller, ITunerErrorListener tunerErrorListener,
ChannelizerType channelizerType)
{
super(controller, tunerErrorListener, channelizerType);
}
@Override
public String getPreferredName()
{
return "HydraSDR " + getController().getDeviceInfo().getSerialNumber();
}
/**
* HydraSDR tuner controller
*/
public HydraSdrTunerController getController()
{
return (HydraSdrTunerController)getTunerController();
}
@Override
public String getUniqueID()
{
try
{
return getController().getDeviceInfo().getSerialNumber();
}
catch(Exception e)
{
mLog.error("error getting serial number", e);
}
return BoardID.HYDRASDR_RFONE.getLabel();
}
@Override
public TunerClass getTunerClass()
{
return TunerClass.HYDRASDR;
}
@Override
public double getSampleSize()
{
return 13.0;
}
@Override
public int getMaximumUSBBitsPerSecond()
{
//4-bytes per sample = 32 bits times 10 MSps = 320,000,000 bits per second
return 320000000;
}
}

View File

@@ -0,0 +1,144 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.source.tuner.hydrasdr;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.source.tuner.TunerType;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerController.Gain;
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
public class HydraSdrTunerConfiguration extends TunerConfiguration
{
private Gain mGain = HydraSdrTunerController.LINEARITY_GAIN_DEFAULT;
private int mSampleRate = HydraSdrTunerController.DEFAULT_SAMPLE_RATE.getRate();
private int mIFGain = HydraSdrTunerController.IF_GAIN_DEFAULT;
private int mMixerGain = HydraSdrTunerController.MIXER_GAIN_DEFAULT;
private int mLNAGain = HydraSdrTunerController.LNA_GAIN_DEFAULT;
private boolean mMixerAGC = false;
private boolean mLNAAGC = false;
private boolean mBiasT = false;
/**
* Default constructor for JAXB
*/
public HydraSdrTunerConfiguration()
{
super(HydraSdrTunerController.MINIMUM_TUNABLE_FREQUENCY_HZ, HydraSdrTunerController.MAXIMUM_TUNABLE_FREQUENCY_HZ);
}
@Override
@JacksonXmlProperty(isAttribute = true, localName = "type", namespace = "http://www.w3.org/2001/XMLSchema-instance")
public TunerType getTunerType()
{
return TunerType.HYDRASDR_R828D;
}
public HydraSdrTunerConfiguration(String uniqueID)
{
super(uniqueID);
}
@JacksonXmlProperty(isAttribute = true, localName = "sample_rate")
public int getSampleRate()
{
return mSampleRate;
}
public void setSampleRate(int sampleRate)
{
mSampleRate = sampleRate;
}
@JacksonXmlProperty(isAttribute = true, localName = "gain")
public Gain getGain()
{
return mGain;
}
public void setGain(Gain gain)
{
mGain = gain;
}
@JacksonXmlProperty(isAttribute = true, localName = "if_gain")
public int getIFGain()
{
return mIFGain;
}
public void setIFGain(int gain)
{
mIFGain = gain;
}
@JacksonXmlProperty(isAttribute = true, localName = "mixer_gain")
public int getMixerGain()
{
return mMixerGain;
}
public void setMixerGain(int gain)
{
mMixerGain = gain;
}
@JacksonXmlProperty(isAttribute = true, localName = "lna_gain")
public int getLNAGain()
{
return mLNAGain;
}
public void setLNAGain(int gain)
{
mLNAGain = gain;
}
@JacksonXmlProperty(isAttribute = true, localName = "mixer_agc")
public boolean isMixerAGC()
{
return mMixerAGC;
}
public void setMixerAGC(boolean enabled)
{
mMixerAGC = enabled;
}
@JacksonXmlProperty(isAttribute = true, localName = "lna_agc")
public boolean isLNAAGC()
{
return mLNAAGC;
}
public void setLNAAGC(boolean enabled)
{
mLNAAGC = enabled;
}
@JacksonXmlProperty(isAttribute = true, localName = "bias_t")
public boolean isBiasT()
{
return mBiasT;
}
public void setBiasT(boolean enabled)
{
mBiasT = enabled;
}
}

View File

@@ -0,0 +1,742 @@
/*
* *****************************************************************************
* Copyright (C) 2014-2024 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
* ****************************************************************************
*/
package io.github.dsheirer.source.tuner.hydrasdr;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerController.Gain;
import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerController.GainMode;
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.manager.TunerStatus;
import io.github.dsheirer.source.tuner.ui.TunerEditor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.List;
import net.miginfocom.swing.MigLayout;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSeparator;
import javax.swing.JSlider;
import javax.swing.SpinnerNumberModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* HydraSDR tuner editor/controller
*/
public class HydraSdrTunerEditor extends TunerEditor<HydraSdrTuner, HydraSdrTunerConfiguration>
{
private static final long serialVersionUID = 1L;
private final static Logger mLog = LoggerFactory.getLogger(HydraSdrTunerEditor.class);
private JButton mTunerInfoButton;
private JComboBox<HydraSdrSampleRate> mSampleRateCombo;
private JComboBox<GainMode> mGainModeCombo;
private JSlider mMasterGainSlider;
private JLabel mMasterGainLabel;
private JLabel mMasterGainValueLabel;
private JSlider mIFGainSlider;
private JLabel mIFGainLabel;
private JLabel mIFGainValueLabel;
private JSlider mLNAGainSlider;
private JLabel mLNAGainValueLabel;
private JSlider mMixerGainSlider;
private JLabel mMixerGainValueLabel;
private JCheckBox mLNAAGCCheckBox;
private JCheckBox mMixerAGCCheckBox;
private JCheckBox mBiasTCheckBox;
/**
* Constructs an instance
* @param userPreferences for wide-band recordings
* @param tunerManager for saving tuner configuration
* @param discoveredTuner for the optionally usable HydraSDR tuner and controller
*/
public HydraSdrTunerEditor(UserPreferences userPreferences, TunerManager tunerManager, DiscoveredTuner discoveredTuner)
{
super(userPreferences, tunerManager, discoveredTuner);
init();
tunerStatusUpdated();
}
@Override
public long getMinimumTunableFrequency()
{
return HydraSdrTunerController.MINIMUM_TUNABLE_FREQUENCY_HZ;
}
@Override
public long getMaximumTunableFrequency()
{
return HydraSdrTunerController.MAXIMUM_TUNABLE_FREQUENCY_HZ;
}
@Override
protected void tunerStatusUpdated()
{
setLoading(true);
if(hasTuner())
{
getTunerIdLabel().setText(getTuner().getPreferredName());
}
else
{
getTunerIdLabel().setText(getDiscoveredTuner().getId());
}
String status = getDiscoveredTuner().getTunerStatus().toString();
if(getDiscoveredTuner().hasErrorMessage())
{
status += " - " + getDiscoveredTuner().getErrorMessage();
}
getTunerStatusLabel().setText(status);
getButtonPanel().updateControls();
getFrequencyPanel().updateControls();
getSampleRateCombo().setEnabled(hasTuner() && !getTuner().getTunerController().isLockedSampleRate());
getTunerInfoButton().setEnabled(hasTuner());
getBiasTCheckBox().setEnabled(hasTuner());
if(hasTuner() && hasConfiguration())
{
getBiasTCheckBox().setSelected(getConfiguration().isBiasT());
}
updateGainComponents((hasTuner() && hasConfiguration()) ? getConfiguration().getGain() : null);
if(hasTuner())
{
List<HydraSdrSampleRate> rates = getTuner().getController().getSampleRates();
getSampleRateCombo().setModel(new DefaultComboBoxModel<>(rates.toArray(new HydraSdrSampleRate[rates.size()])));
if(hasConfiguration())
{
HydraSdrSampleRate sampleRate = getSampleRate(getConfiguration().getSampleRate());
getSampleRateCombo().setSelectedItem(sampleRate);
}
}
else
{
getSampleRateCombo().setModel(new DefaultComboBoxModel<>());
}
setLoading(false);
}
private void init()
{
setLayout(new MigLayout("fill,wrap 3", "[right][grow,fill][fill]",
"[][][][][][][][][][][][][][][][grow]"));
add(new JLabel("Tuner:"));
add(getTunerIdLabel());
add(getTunerInfoButton());
add(new JLabel("Status:"));
add(getTunerStatusLabel(), "wrap");
add(getButtonPanel(), "span,align left");
add(new JSeparator(), "span,growx,push");
add(new JLabel("Frequency (MHz):"));
add(getFrequencyPanel(), "wrap");
add(new JLabel("Sample Rate:"));
add(getSampleRateCombo(), "wrap");
add(new JLabel());
add(getBiasTCheckBox(), "wrap");
add(new JSeparator(), "span,growx,push");
add(new JLabel("Gain Control"), "wrap");
add(new JLabel("Mode:"));
add(getGainModeCombo(), "wrap");
add(getMasterGainLabel());
add(getMasterGainSlider());
add(getMasterGainValueLabel());
add(getIFGainLabel());
add(getIFGainSlider());
add(getIFGainValueLabel());
add(getMixerAGCCheckBox());
add(getMixerGainSlider());
add(getMixerGainValueLabel());
add(getLNAAGCCheckBox());
add(getLNAGainSlider());
add(getLNAGainValueLabel());
}
private JCheckBox getLNAAGCCheckBox()
{
if(mLNAAGCCheckBox == null)
{
mLNAAGCCheckBox = new JCheckBox("AGC LNA:");
mLNAAGCCheckBox.setEnabled(false);
mLNAAGCCheckBox.addActionListener(e ->
{
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setLNAAGC(getLNAAGCCheckBox().isSelected());
getLNAGainSlider().setEnabled(!getLNAAGCCheckBox().isSelected());
save();
}
catch(Exception e1)
{
mLog.error("Error setting LNA AGC Enabled");
}
}
});
}
return mLNAAGCCheckBox;
}
private JCheckBox getMixerAGCCheckBox()
{
if(mMixerAGCCheckBox == null)
{
mMixerAGCCheckBox = new JCheckBox("AGC Mixer:");
mMixerAGCCheckBox.setEnabled(false);
mMixerAGCCheckBox.addActionListener(e ->
{
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setMixerAGC(getMixerAGCCheckBox().isSelected());
getMixerGainSlider().setEnabled(!getMixerAGCCheckBox().isSelected());
save();
}
catch(Exception e1)
{
mLog.error("Error setting Mixer AGC Enabled");
}
}
});
}
return mMixerAGCCheckBox;
}
/**
* Checkbox to enable/disable Bias-T power for active antennas
*/
private JCheckBox getBiasTCheckBox()
{
if(mBiasTCheckBox == null)
{
mBiasTCheckBox = new JCheckBox("Bias-T");
mBiasTCheckBox.setEnabled(false);
mBiasTCheckBox.setToolTipText("Enable Bias-T power output for active antennas");
mBiasTCheckBox.addActionListener(e ->
{
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setBiasT(mBiasTCheckBox.isSelected());
save();
}
catch(Exception e1)
{
mLog.error("Error setting Bias-T", e1);
JOptionPane.showMessageDialog(mBiasTCheckBox, "Couldn't set Bias-T: " + e1.getMessage());
}
}
});
}
return mBiasTCheckBox;
}
private JLabel getLNAGainValueLabel()
{
if(mLNAGainValueLabel == null)
{
mLNAGainValueLabel = new JLabel("0");
mLNAGainValueLabel.setEnabled(false);
}
return mLNAGainValueLabel;
}
private JSlider getLNAGainSlider()
{
if(mLNAGainSlider == null)
{
mLNAGainSlider = new JSlider(JSlider.HORIZONTAL, HydraSdrTunerController.LNA_GAIN_MIN,
HydraSdrTunerController.LNA_GAIN_MAX, HydraSdrTunerController.LNA_GAIN_MIN);
mLNAGainSlider.setEnabled(false);
mLNAGainSlider.setMajorTickSpacing(1);
mLNAGainSlider.setPaintTicks(true);
mLNAGainSlider.addChangeListener(event ->
{
int gain = mLNAGainSlider.getValue();
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setLNAGain(gain);
save();
}
catch(Exception e)
{
mLog.error("Couldn't set HydraSDR LNA gain to:" + gain, e);
JOptionPane.showMessageDialog(mLNAGainSlider, "Couldn't set LNA gain value to " + gain);
}
}
getLNAGainValueLabel().setText(String.valueOf(gain));
});
}
return mLNAGainSlider;
}
private JLabel getMixerGainValueLabel()
{
if(mMixerGainValueLabel == null)
{
mMixerGainValueLabel = new JLabel("0");
mMixerGainValueLabel.setEnabled(false);
}
return mMixerGainValueLabel;
}
private JSlider getMixerGainSlider()
{
if(mMixerGainSlider == null)
{
mMixerGainSlider = new JSlider(JSlider.HORIZONTAL, HydraSdrTunerController.MIXER_GAIN_MIN,
HydraSdrTunerController.MIXER_GAIN_MAX, HydraSdrTunerController.MIXER_GAIN_MIN);
mMixerGainSlider.setEnabled(false);
mMixerGainSlider.setMajorTickSpacing(1);
mMixerGainSlider.setPaintTicks(true);
mMixerGainSlider.addChangeListener(new ChangeListener()
{
@Override
public void stateChanged(ChangeEvent event)
{
int gain = mMixerGainSlider.getValue();
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setMixerGain(gain);
save();
}
catch(Exception e)
{
mLog.error("Couldn't set HydraSDR Mixer gain to:" + gain, e);
JOptionPane.showMessageDialog(mMixerGainSlider, "Couldn't set Mixer gain value to " + gain);
}
}
getMixerGainValueLabel().setText(String.valueOf(gain));
}
});
}
return mMixerGainSlider;
}
private JLabel getIFGainLabel()
{
if(mIFGainLabel == null)
{
mIFGainLabel = new JLabel("IF:");
}
return mIFGainLabel;
}
private JLabel getIFGainValueLabel()
{
if(mIFGainValueLabel == null)
{
mIFGainValueLabel = new JLabel("0");
mIFGainValueLabel.setEnabled(false);
}
return mIFGainValueLabel;
}
private JSlider getIFGainSlider()
{
if(mIFGainSlider == null)
{
mIFGainSlider = new JSlider(JSlider.HORIZONTAL, HydraSdrTunerController.IF_GAIN_MIN,
HydraSdrTunerController.IF_GAIN_MAX, HydraSdrTunerController.IF_GAIN_MIN);
mIFGainSlider.setEnabled(false);
mIFGainSlider.setMajorTickSpacing(1);
mIFGainSlider.setPaintTicks(true);
mIFGainSlider.addChangeListener(event ->
{
int gain = mIFGainSlider.getValue();
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setIFGain(gain);
save();
}
catch(Exception e)
{
mLog.error("Couldn't set HydraSDR IF gain to:" + gain, e);
JOptionPane.showMessageDialog(mIFGainSlider, "Couldn't set IF gain value to " + gain);
}
}
getIFGainValueLabel().setText(String.valueOf(gain));
});
}
return mIFGainSlider;
}
private JLabel getMasterGainLabel()
{
if(mMasterGainLabel == null)
{
mMasterGainLabel = new JLabel("Master:");
}
return mMasterGainLabel;
}
private JLabel getMasterGainValueLabel()
{
if(mMasterGainValueLabel == null)
{
mMasterGainValueLabel = new JLabel("0");
mMasterGainValueLabel.setEnabled(false);
}
return mMasterGainValueLabel;
}
private JSlider getMasterGainSlider()
{
if(mMasterGainSlider == null)
{
mMasterGainSlider = new JSlider(JSlider.HORIZONTAL, HydraSdrTunerController.GAIN_MIN,
HydraSdrTunerController.GAIN_MAX, HydraSdrTunerController.GAIN_MIN);
mMasterGainSlider.setEnabled(false);
mMasterGainSlider.setMajorTickSpacing(1);
mMasterGainSlider.setPaintTicks(true);
mMasterGainSlider.addChangeListener(event ->
{
GainMode mode = (GainMode)mGainModeCombo.getSelectedItem();
int value = mMasterGainSlider.getValue();
Gain gain = Gain.getGain(mode, value);
if(hasTuner() && !isLoading())
{
try
{
getTuner().getController().setGain(gain);
save();
}
catch(Exception e)
{
mLog.error("Couldn't set HydraSDR gain to:" + gain.name(), e);
JOptionPane.showMessageDialog(mMasterGainSlider, "Couldn't set gain value to " +
gain.getValue());
}
}
getMasterGainValueLabel().setText(String.valueOf(value));
});
}
return mMasterGainSlider;
}
private JComboBox<GainMode> getGainModeCombo()
{
if(mGainModeCombo == null)
{
mGainModeCombo = new JComboBox<>(GainMode.values());
mGainModeCombo.setEnabled(false);
mGainModeCombo.addActionListener(e ->
{
if(hasTuner() && !isLoading())
{
GainMode mode = (GainMode)mGainModeCombo.getSelectedItem();
int value = getMasterGainSlider().getValue();
Gain gain = Gain.getGain(mode, value);
updateGainComponents(gain);
save();
}
});
}
return mGainModeCombo;
}
private JComboBox<HydraSdrSampleRate> getSampleRateCombo()
{
if(mSampleRateCombo == null)
{
mSampleRateCombo = new JComboBox<>();
mSampleRateCombo.setEnabled(false);
mSampleRateCombo.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
if(hasTuner() && !isLoading())
{
HydraSdrSampleRate rate = (HydraSdrSampleRate)mSampleRateCombo.getSelectedItem();
try
{
getTuner().getController().setSampleRate(rate);
//Adjust the min/max values for the sample rate.
adjustForSampleRate(rate.getRate());
save();
}
catch(Exception e1)
{
JOptionPane.showMessageDialog(HydraSdrTunerEditor.this,
"Couldn't set sample rate to " + rate.getLabel());
mLog.error("Error setting HydraSDR sample rate", e1);
}
}
}
});
}
return mSampleRateCombo;
}
/**
* Hyperlink button that provides tuner information
*/
private JButton getTunerInfoButton()
{
if(mTunerInfoButton == null)
{
mTunerInfoButton = new JButton("Info");
mTunerInfoButton.setEnabled(false);
mTunerInfoButton.addActionListener(e -> JOptionPane.showMessageDialog(HydraSdrTunerEditor.this,
getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE));
}
return mTunerInfoButton;
}
/**
* Updates the enabled state of each of the gain controls according to the
* specified gain mode. The master gain control is enabled for linearity
* and sensitivity and the individual gain controls are disabled, and
* vice-versa for custom mode.
*/
private void updateGainComponents(Gain gain)
{
if(hasTuner() && gain != null)
{
boolean isCustom = gain.equals(Gain.CUSTOM);
getGainModeCombo().setEnabled(true);
getGainModeCombo().setSelectedItem(gain.getGainMode());
getMasterGainLabel().setEnabled(!isCustom);
getMasterGainSlider().setEnabled(!isCustom);
getMasterGainSlider().setValue(gain.getValue());
getMasterGainValueLabel().setEnabled(!isCustom);
getIFGainLabel().setEnabled(isCustom);
getIFGainSlider().setEnabled(isCustom);
getIFGainValueLabel().setEnabled(isCustom);
getLNAAGCCheckBox().setEnabled(isCustom);
getLNAGainSlider().setEnabled(isCustom && !getConfiguration().isLNAAGC());
getLNAGainValueLabel().setEnabled(isCustom);
getMixerAGCCheckBox().setEnabled(isCustom);
getMixerGainSlider().setEnabled(isCustom && !getConfiguration().isMixerAGC());
getMixerGainValueLabel().setEnabled(isCustom);
if(isCustom)
{
getIFGainSlider().setValue(getConfiguration().getIFGain());
getLNAGainSlider().setValue(getConfiguration().getLNAGain());
getMixerGainSlider().setValue(getConfiguration().getMixerGain());
getMixerAGCCheckBox().setSelected(getConfiguration().isMixerAGC());
getLNAAGCCheckBox().setSelected(getConfiguration().isLNAAGC());
}
else
{
getIFGainSlider().setValue(0);
getLNAGainSlider().setValue(0);
getMixerGainSlider().setValue(0);
getMixerAGCCheckBox().setSelected(false);
getLNAAGCCheckBox().setSelected(false);
}
}
else
{
getGainModeCombo().setEnabled(false);
getMasterGainLabel().setEnabled(false);
getMasterGainSlider().setEnabled(false);
getMasterGainSlider().setValue(0);
getMasterGainValueLabel().setEnabled(false);
getIFGainLabel().setEnabled(false);
getIFGainSlider().setEnabled(false);
getIFGainSlider().setValue(0);
getIFGainValueLabel().setEnabled(false);
getLNAAGCCheckBox().setEnabled(false);
getLNAAGCCheckBox().setSelected(false);
getLNAGainSlider().setEnabled(false);
getLNAGainSlider().setValue(0);
getLNAGainValueLabel().setEnabled(false);
getMixerAGCCheckBox().setEnabled(false);
getMixerAGCCheckBox().setSelected(false);
getMixerGainSlider().setEnabled(false);
getMixerGainSlider().setValue(0);
getMixerGainValueLabel().setEnabled(false);
}
}
@Override
public void save()
{
if(hasConfiguration() && !isLoading())
{
getConfiguration().setFrequency(getFrequencyControl().getFrequency());
getConfiguration().setMinimumFrequency(getMinimumFrequencyTextField().getFrequency());
getConfiguration().setMaximumFrequency(getMaximumFrequencyTextField().getFrequency());
double value = ((SpinnerNumberModel) getFrequencyCorrectionSpinner().getModel()).getNumber().doubleValue();
getConfiguration().setFrequencyCorrection(value);
getConfiguration().setAutoPPMCorrectionEnabled(getAutoPPMCheckBox().isSelected());
getConfiguration().setSampleRate(((HydraSdrSampleRate)getSampleRateCombo().getSelectedItem()).getRate());
Gain gain = Gain.getGain((GainMode)mGainModeCombo.getSelectedItem(), getMasterGainSlider().getValue());
getConfiguration().setGain(gain);
getConfiguration().setIFGain(getIFGainSlider().getValue());
getConfiguration().setMixerGain(getMixerGainSlider().getValue());
getConfiguration().setLNAGain(getLNAGainSlider().getValue());
getConfiguration().setMixerAGC(getMixerAGCCheckBox().isSelected());
getConfiguration().setLNAAGC(getLNAAGCCheckBox().isSelected());
getConfiguration().setBiasT(getBiasTCheckBox().isSelected());
saveConfiguration();
}
}
/**
* Finds the HydraSDR sample rate entry that matches the value.
* @param value in Hertz
* @return the matching rate entry or null.
*/
private HydraSdrSampleRate getSampleRate(int value)
{
if(hasTuner())
{
List<HydraSdrSampleRate> rates = getTuner().getController().getSampleRates();
for(HydraSdrSampleRate rate : rates)
{
if(rate.getRate() == value)
{
return rate;
}
}
if(rates.size() > 0)
{
return rates.get(0);
}
}
return null;
}
/**
* Updates the sample rate tooltip according to the tuner controller's lock state.
*/
private void updateSampleRateToolTip()
{
if(hasTuner() && getTuner().getController().isLockedSampleRate())
{
getSampleRateCombo().setToolTipText("Sample Rate is locked. Disable decoding channels to unlock.");
}
else
{
getSampleRateCombo().setToolTipText("Select a sample rate for the tuner");
}
}
@Override
public void setTunerLockState(boolean locked)
{
getFrequencyPanel().updateControls();
getSampleRateCombo().setEnabled(!locked);
updateSampleRateToolTip();
}
private String getTunerInfo()
{
if(getDiscoveredTuner().getTunerStatus() == TunerStatus.ERROR)
{
return getDiscoveredTuner().getErrorMessage();
}
if(hasTuner())
{
StringBuilder sb = new StringBuilder();
HydraSdrDeviceInformation info = getTuner().getController().getDeviceInfo();
sb.append("<html><h3>HydraSDR Tuner</h3>");
sb.append("<b>Serial: </b>");
sb.append(info.getSerialNumber());
sb.append("<br>");
sb.append("<b>Firmware: </b>");
String[] firmware = info.getVersion().split(" ");
sb.append(firmware.length > 1 ? firmware[0] : info.getVersion());
sb.append("<br>");
sb.append("<b>Part: </b>");
sb.append(info.getPartNumber());
sb.append("<br>");
sb.append("<b>Board ID: </b>");
sb.append(info.getBoardID().getLabel());
sb.append("<br>");
return sb.toString();
}
return null;
}
}

View File

@@ -22,9 +22,6 @@ package io.github.dsheirer.vector.calibrate;
import io.github.dsheirer.dsp.am.AmDemodulatorCalibration;
import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.preference.calibration.VectorCalibrationPreference;
import io.github.dsheirer.vector.calibrate.airspy.AirspySampleConverterCalibration;
import io.github.dsheirer.vector.calibrate.airspy.AirspyUnpackedCalibration;
import io.github.dsheirer.vector.calibrate.airspy.AirspyUnpackedInterleavedCalibration;
import io.github.dsheirer.vector.calibrate.demodulator.DifferentialDemodulatorCalibration;
import io.github.dsheirer.vector.calibrate.demodulator.FmDemodulatorCalibration;
import io.github.dsheirer.vector.calibrate.filter.FirFilterCalibration;
@@ -41,6 +38,9 @@ import io.github.dsheirer.vector.calibrate.magnitude.MagnitudeCalibration;
import io.github.dsheirer.vector.calibrate.mixer.ComplexMixerCalibration;
import io.github.dsheirer.vector.calibrate.oscillator.ComplexOscillatorCalibration;
import io.github.dsheirer.vector.calibrate.oscillator.RealOscillatorCalibration;
import io.github.dsheirer.vector.calibrate.sample.PackedSampleConverterCalibration;
import io.github.dsheirer.vector.calibrate.sample.UnpackedInterleavedSampleConverterCalibration;
import io.github.dsheirer.vector.calibrate.sample.UnpackedSampleConverterCalibration;
import io.github.dsheirer.vector.calibrate.sync.DMRSoftSyncCalibration;
import io.github.dsheirer.vector.calibrate.sync.P25P1SoftSyncCalibration;
import io.github.dsheirer.vector.calibrate.window.WindowCalibration;
@@ -100,9 +100,9 @@ public class CalibrationManager
sInstance = new CalibrationManager();
sInstance.add(new AirspySampleConverterCalibration());
sInstance.add(new AirspyUnpackedCalibration());
sInstance.add(new AirspyUnpackedInterleavedCalibration());
sInstance.add(new PackedSampleConverterCalibration());
sInstance.add(new UnpackedSampleConverterCalibration());
sInstance.add(new UnpackedInterleavedSampleConverterCalibration());
sInstance.add(new AmDemodulatorCalibration());
sInstance.add(new ComplexGainCalibration());
sInstance.add(new ComplexGainControlCalibration());

View File

@@ -32,9 +32,6 @@ public enum CalibrationType
OSCILLATOR_COMPLEX("Complex Oscillator", 1),
GAIN_COMPLEX("Complex Gain", 1),
AIRSPY_SAMPLE_CONVERTER("Airspy Sample Converter", 1),
AIRSPY_UNPACKED_INTERLEAVED_ITERATOR("Airspy Unpacked Interleaved Iterator", 1),
AIRSPY_UNPACKED_ITERATOR("Airspy Unpacked Iterator", 1),
AM_DEMODULATOR("AM Demodulator", 1),
DC_REMOVAL_REAL("Real DC Removal Filter", 1),
DMR_SOFT_SYNC_DETECTOR("DMR Soft Sync Detector", 1),
@@ -53,6 +50,9 @@ public enum CalibrationType
MIXER_COMPLEX("Complex Mixer", 1),
OSCILLATOR_REAL("Real Oscillator", 1),
P25P1_SOFT_SYNC_DETECTOR("P25 Phase 1 Soft Sync Detector", 1),
SAMPLE_PACKED_CONVERTER("Packed Sample Converter", 1),
SAMPLE_UNPACKED_INTERLEAVED_ITERATOR("Unpacked Interleaved Sample Iterator", 1),
SAMPLE_UNPACKED_ITERATOR("Unpacked Sample Iterator", 1),
SQUELCHING_FM_DEMODULATOR("Squelching FM Demodulator", 1),
WINDOW("Window", 1);

View File

@@ -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,19 +17,18 @@
* ****************************************************************************
*/
package io.github.dsheirer.vector.calibrate.airspy;
package io.github.dsheirer.vector.calibrate.sample;
import io.github.dsheirer.buffer.airspy.ScalarUnpackedSampleConverter;
import io.github.dsheirer.buffer.airspy.VectorUnpackedSampleConverter;
import io.github.dsheirer.buffer.sample.ScalarUnpackedSampleConverter;
import io.github.dsheirer.buffer.sample.VectorUnpackedSampleConverter;
import io.github.dsheirer.vector.calibrate.Calibration;
import io.github.dsheirer.vector.calibrate.CalibrationException;
import io.github.dsheirer.vector.calibrate.CalibrationType;
import io.github.dsheirer.vector.calibrate.Implementation;
import java.nio.ByteBuffer;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
import java.nio.ByteBuffer;
public class AirspySampleConverterCalibration extends Calibration
public class PackedSampleConverterCalibration extends Calibration
{
private static final int BUFFER_SIZE = 262144;
private static final int ITERATION_DURATION_MS = 1000;
@@ -39,9 +38,9 @@ public class AirspySampleConverterCalibration extends Calibration
/**
* Constructs an instance
*/
public AirspySampleConverterCalibration()
public PackedSampleConverterCalibration()
{
super(CalibrationType.AIRSPY_SAMPLE_CONVERTER);
super(CalibrationType.SAMPLE_PACKED_CONVERTER);
}
@Override
@@ -59,7 +58,7 @@ public class AirspySampleConverterCalibration extends Calibration
scalarMean.increment(score);
}
mLog.info("AIRSPY CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
mLog.info("PACKED SAMPLE CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
for(int vectorTest = 0; vectorTest < WARM_UP_ITERATIONS; vectorTest++)
{
@@ -67,7 +66,7 @@ public class AirspySampleConverterCalibration extends Calibration
vectorMean.increment(score);
}
mLog.info("AIRSPY CONVERTER WARMUP - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("PACKED SAMPLE CONVERTER WARMUP - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
scalarMean.clear();
vectorMean.clear();
@@ -78,7 +77,7 @@ public class AirspySampleConverterCalibration extends Calibration
scalarMean.increment(score);
}
mLog.info("AIRSPY CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
mLog.info("PACKED SAMPLE CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
for(int vectorTest = 0; vectorTest < TEST_ITERATIONS; vectorTest++)
{
@@ -86,7 +85,7 @@ public class AirspySampleConverterCalibration extends Calibration
vectorMean.increment(score);
}
mLog.info("AIRSPY CONVERTER - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("PACKED SAMPLE CONVERTER - VECTOR: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
if(scalarMean.getResult() > vectorMean.getResult())
{
@@ -97,7 +96,7 @@ public class AirspySampleConverterCalibration extends Calibration
setImplementation(Implementation.VECTOR_SIMD_PREFERRED);
}
mLog.info("AIRSPY CONVERTER - SETTING OPTIMAL IMPLEMENTATION TO: " + getImplementation());
mLog.info("PACKED SAMPLE CONVERTER - SETTING OPTIMAL IMPLEMENTATION TO: " + getImplementation());
}
private long testScalar(ByteBuffer buffer)

View File

@@ -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,14 +17,14 @@
* ****************************************************************************
*/
package io.github.dsheirer.vector.calibrate.airspy;
package io.github.dsheirer.vector.calibrate.sample;
import io.github.dsheirer.buffer.airspy.AirspyBufferIterator;
import io.github.dsheirer.buffer.airspy.AirspyInterleavedBufferIteratorScalar;
import io.github.dsheirer.buffer.airspy.AirspyInterleavedBufferIteratorVector128Bits;
import io.github.dsheirer.buffer.airspy.AirspyInterleavedBufferIteratorVector256Bits;
import io.github.dsheirer.buffer.airspy.AirspyInterleavedBufferIteratorVector512Bits;
import io.github.dsheirer.buffer.airspy.AirspyInterleavedBufferIteratorVector64Bits;
import io.github.dsheirer.buffer.sample.SampleBufferIterator;
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorScalar;
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector128Bits;
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector256Bits;
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector512Bits;
import io.github.dsheirer.buffer.sample.SampleInterleavedBufferIteratorVector64Bits;
import io.github.dsheirer.vector.calibrate.Calibration;
import io.github.dsheirer.vector.calibrate.CalibrationException;
import io.github.dsheirer.vector.calibrate.CalibrationType;
@@ -33,9 +33,9 @@ import jdk.incubator.vector.FloatVector;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
/**
* Calculates optimal implementation (SCALAR vs VECTOR) for interleaved airspy native buffers.
* Calculates optimal implementation (SCALAR vs VECTOR) for interleaved sample native buffers.
*/
public class AirspyUnpackedInterleavedCalibration extends Calibration
public class UnpackedInterleavedSampleConverterCalibration extends Calibration
{
private static final int BUFFER_SIZE = 131072;
private static final int ITERATION_DURATION_MS = 1000;
@@ -45,20 +45,20 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
/**
* Constructs an instance
*/
public AirspyUnpackedInterleavedCalibration()
public UnpackedInterleavedSampleConverterCalibration()
{
super(CalibrationType.AIRSPY_UNPACKED_INTERLEAVED_ITERATOR);
super(CalibrationType.SAMPLE_UNPACKED_INTERLEAVED_ITERATOR);
}
@Override
public void calibrate() throws CalibrationException
{
short[] samples = getShortSamples(BUFFER_SIZE);
short[] residualI = getShortSamples(AirspyBufferIterator.I_OVERLAP);
short[] residualQ = getShortSamples(AirspyBufferIterator.Q_OVERLAP);
short[] residualI = getShortSamples(SampleBufferIterator.I_OVERLAP);
short[] residualQ = getShortSamples(SampleBufferIterator.Q_OVERLAP);
//Warm-Up Phase ....
mLog.info("AIRSPY UNPACKED INTERLEAVED - VECTOR SIMD LANES PREFERRED: " + FloatVector.SPECIES_PREFERRED.length());
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR SIMD LANES PREFERRED: " + FloatVector.SPECIES_PREFERRED.length());
Mean scalarMean = new Mean();
@@ -68,7 +68,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
scalarMean.increment(score);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
switch(FloatVector.SPECIES_PREFERRED.length())
{
@@ -84,7 +84,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
vectorMean.increment(score);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
case 8:
{
@@ -96,7 +96,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
vectorMean.increment(score);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
case 4:
{
@@ -108,7 +108,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
vectorMean.increment(score);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
case 2:
{
@@ -120,7 +120,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
vectorMean.increment(score);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
//Test Phase ....
@@ -134,7 +134,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
double bestScore = scalarMean.getResult();
setImplementation(Implementation.SCALAR);
mLog.info("AIRSPY UNPACKED INTERLEAVED - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
switch(FloatVector.SPECIES_PREFERRED.length())
{
@@ -156,7 +156,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
setImplementation(Implementation.VECTOR_SIMD_512);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 512: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
case 8:
{
@@ -174,7 +174,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
setImplementation(Implementation.VECTOR_SIMD_256);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 256: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
case 4:
{
@@ -192,7 +192,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
setImplementation(Implementation.VECTOR_SIMD_128);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 128: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
case 2:
{
@@ -209,12 +209,12 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
setImplementation(Implementation.VECTOR_SIMD_64);
}
mLog.info("AIRSPY UNPACKED INTERLEAVED - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - VECTOR 64: " + DECIMAL_FORMAT.format(vectorMean.getResult()));
}
}
}
mLog.info("AIRSPY UNPACKED INTERLEAVED - SET OPTIMAL IMPLEMENTATION TO: " + getImplementation());
mLog.info("UNPACKED INTERLEAVED SAMPLE CONVERTER - SET OPTIMAL IMPLEMENTATION TO: " + getImplementation());
}
private long calibrateScalar(short[] samples, short[] residualI, short[] residualQ)
@@ -224,7 +224,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
long count = 0;
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyInterleavedBufferIteratorScalar iterator = new AirspyInterleavedBufferIteratorScalar(samples, residualI,
SampleInterleavedBufferIteratorScalar iterator = new SampleInterleavedBufferIteratorScalar(samples, residualI,
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -245,7 +245,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyInterleavedBufferIteratorVector64Bits iterator = new AirspyInterleavedBufferIteratorVector64Bits(samples, residualI,
SampleInterleavedBufferIteratorVector64Bits iterator = new SampleInterleavedBufferIteratorVector64Bits(samples, residualI,
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -266,7 +266,7 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
for(int x = 0; x < ITERATION_DURATION_MS; x++)
{
AirspyInterleavedBufferIteratorVector128Bits iterator = new AirspyInterleavedBufferIteratorVector128Bits(samples, residualI,
SampleInterleavedBufferIteratorVector128Bits iterator = new SampleInterleavedBufferIteratorVector128Bits(samples, residualI,
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -287,8 +287,8 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyInterleavedBufferIteratorVector256Bits iterator =
new AirspyInterleavedBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
SampleInterleavedBufferIteratorVector256Bits iterator =
new SampleInterleavedBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
{
@@ -308,8 +308,8 @@ public class AirspyUnpackedInterleavedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyInterleavedBufferIteratorVector512Bits iterator =
new AirspyInterleavedBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
SampleInterleavedBufferIteratorVector512Bits iterator =
new SampleInterleavedBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))

View File

@@ -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,14 +17,14 @@
* ****************************************************************************
*/
package io.github.dsheirer.vector.calibrate.airspy;
package io.github.dsheirer.vector.calibrate.sample;
import io.github.dsheirer.buffer.airspy.AirspyBufferIterator;
import io.github.dsheirer.buffer.airspy.AirspyBufferIteratorScalar;
import io.github.dsheirer.buffer.airspy.AirspyBufferIteratorVector128Bits;
import io.github.dsheirer.buffer.airspy.AirspyBufferIteratorVector256Bits;
import io.github.dsheirer.buffer.airspy.AirspyBufferIteratorVector512Bits;
import io.github.dsheirer.buffer.airspy.AirspyBufferIteratorVector64Bits;
import io.github.dsheirer.buffer.sample.SampleBufferIterator;
import io.github.dsheirer.buffer.sample.SampleBufferIteratorScalar;
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector128Bits;
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector256Bits;
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector512Bits;
import io.github.dsheirer.buffer.sample.SampleBufferIteratorVector64Bits;
import io.github.dsheirer.vector.calibrate.Calibration;
import io.github.dsheirer.vector.calibrate.CalibrationException;
import io.github.dsheirer.vector.calibrate.CalibrationType;
@@ -33,9 +33,9 @@ import jdk.incubator.vector.FloatVector;
import org.apache.commons.math3.stat.descriptive.moment.Mean;
/**
* Calculates optimal implementation (SCALAR vs VECTOR) for non-interleaved airspy native buffers.
* Calculates optimal implementation (SCALAR vs VECTOR) for non-interleaved sample native buffers.
*/
public class AirspyUnpackedCalibration extends Calibration
public class UnpackedSampleConverterCalibration extends Calibration
{
private static final int BUFFER_SIZE = 131072;
private static final int ITERATION_DURATION_MS = 1000;
@@ -45,19 +45,19 @@ public class AirspyUnpackedCalibration extends Calibration
/**
* Constructs an instance
*/
public AirspyUnpackedCalibration()
public UnpackedSampleConverterCalibration()
{
super(CalibrationType.AIRSPY_UNPACKED_ITERATOR);
super(CalibrationType.SAMPLE_UNPACKED_ITERATOR);
}
@Override
public void calibrate() throws CalibrationException
{
short[] samples = getShortSamples(BUFFER_SIZE);
short[] residualI = getShortSamples(AirspyBufferIterator.I_OVERLAP);
short[] residualQ = getShortSamples(AirspyBufferIterator.Q_OVERLAP);
short[] residualI = getShortSamples(SampleBufferIterator.I_OVERLAP);
short[] residualQ = getShortSamples(SampleBufferIterator.Q_OVERLAP);
mLog.info("AIRSPY UNPACKED - VECTOR SIMD LANES PREFERRED: " + FloatVector.SPECIES_PREFERRED.length());
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR SIMD LANES PREFERRED: " + FloatVector.SPECIES_PREFERRED.length());
//Warm-Up Phase ....
Mean scalarMean = new Mean();
@@ -68,7 +68,7 @@ public class AirspyUnpackedCalibration extends Calibration
scalarMean.increment(score);
}
mLog.info("AIRSPY UNPACKED WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
switch(FloatVector.SPECIES_PREFERRED.length())
{
@@ -83,7 +83,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector512Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
}
case 8:
{
@@ -94,7 +94,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector256Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
}
case 4:
{
@@ -105,7 +105,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector128Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
}
case 2:
{
@@ -116,7 +116,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector64Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER WARMUP - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
}
}
@@ -131,7 +131,7 @@ public class AirspyUnpackedCalibration extends Calibration
double bestScore = scalarMean.getResult();
setImplementation(Implementation.SCALAR);
mLog.info("AIRSPY UNPACKED - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER - SCALAR: " + DECIMAL_FORMAT.format(scalarMean.getResult()));
switch(FloatVector.SPECIES_PREFERRED.length())
{
@@ -146,7 +146,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector512Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 512: " + DECIMAL_FORMAT.format(vector512Mean.getResult()));
if(vector512Mean.getResult() > bestScore)
{
bestScore = vector512Mean.getResult();
@@ -162,7 +162,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector256Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 256: " + DECIMAL_FORMAT.format(vector256Mean.getResult()));
if(vector256Mean.getResult() > bestScore)
{
bestScore = vector256Mean.getResult();
@@ -178,7 +178,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector128Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 128: " + DECIMAL_FORMAT.format(vector128Mean.getResult()));
if(vector128Mean.getResult() > bestScore)
{
bestScore = vector128Mean.getResult();
@@ -194,7 +194,7 @@ public class AirspyUnpackedCalibration extends Calibration
vector64Mean.increment(score);
}
mLog.info("AIRSPY UNPACKED - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
mLog.info("UNPACKED SAMPLE CONVERTER - VECTOR 64: " + DECIMAL_FORMAT.format(vector64Mean.getResult()));
if(vector64Mean.getResult() > bestScore)
{
setImplementation(Implementation.VECTOR_SIMD_64);
@@ -202,7 +202,7 @@ public class AirspyUnpackedCalibration extends Calibration
}
}
mLog.info("AIRSPY UNPACKED - SET OPTIMAL IMPLEMENTATION TO: " + getImplementation());
mLog.info("UNPACKED SAMPLE CONVERTER - SET OPTIMAL IMPLEMENTATION TO: " + getImplementation());
}
private long testScalar(short[] samples, short[] residualI, short[] residualQ)
@@ -213,7 +213,7 @@ public class AirspyUnpackedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyBufferIteratorScalar iterator = new AirspyBufferIteratorScalar(samples, residualI,
SampleBufferIteratorScalar iterator = new SampleBufferIteratorScalar(samples, residualI,
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -234,7 +234,7 @@ public class AirspyUnpackedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyBufferIteratorVector64Bits iterator = new AirspyBufferIteratorVector64Bits(samples, residualI,
SampleBufferIteratorVector64Bits iterator = new SampleBufferIteratorVector64Bits(samples, residualI,
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -254,7 +254,7 @@ public class AirspyUnpackedCalibration extends Calibration
long count = 0;
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyBufferIteratorVector128Bits iterator = new AirspyBufferIteratorVector128Bits(samples, residualI,
SampleBufferIteratorVector128Bits iterator = new SampleBufferIteratorVector128Bits(samples, residualI,
residualQ, 0.0f, System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -275,8 +275,8 @@ public class AirspyUnpackedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyBufferIteratorVector256Bits iterator =
new AirspyBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f,
SampleBufferIteratorVector256Bits iterator =
new SampleBufferIteratorVector256Bits(samples, residualI, residualQ, 0.0f,
System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))
@@ -297,8 +297,8 @@ public class AirspyUnpackedCalibration extends Calibration
while((System.currentTimeMillis() - start) < ITERATION_DURATION_MS)
{
AirspyBufferIteratorVector512Bits iterator =
new AirspyBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
SampleBufferIteratorVector512Bits iterator =
new SampleBufferIteratorVector512Bits(samples, residualI, residualQ, 0.0f,
System.currentTimeMillis(), 0.0f);
while(iterator.hasNext() && ((System.currentTimeMillis() - start) < ITERATION_DURATION_MS))

View File

@@ -0,0 +1,11 @@
# HydraSDR RFOne udev rules - Universal cross-distribution compatibility
# Works on: Ubuntu, Debian, Fedora, AlmaLinux, openSUSE, Arch Linux, and more
# Supports: systemd (modern) and legacy systems
# Note: Due to rule ordering, the fallback GROUP="users" overrides the primary GROUP="plugdev" even on distros where plugdev exists.
# This doesn't break functionality thanks to uaccess tags, but for stricter security, consider revising to a single rule per device with GROUP="root", MODE="0660", and TAG+="uaccess" to rely solely on ACLs.
# HydraSDR RFOne main device (idVendor=38af VERNOUX BENJAMIN, idProduct=0001 HydraSDR RFOne Normal operation mode)
# Primary rule for distributions with plugdev group (Debian/Ubuntu family)
SUBSYSTEM=="usb", ATTR{idVendor}=="38af", ATTR{idProduct}=="0001", SYMLINK+="hydrasdr_rfone-%k", MODE="0660", GROUP="plugdev", TAG+="uaccess", TAG+="udev-acl"
# Fallback rule for distributions without plugdev group (Red Hat/SUSE/Arch family)
SUBSYSTEM=="usb", ATTR{idVendor}=="38af", ATTR{idProduct}=="0001", SYMLINK+="hydrasdr_rfone-%k", MODE="0664", GROUP="users", TAG+="uaccess"