diff --git a/build.gradle b/build.gradle
index 33b3045d..81021e5c 100644
--- a/build.gradle
+++ b/build.gradle
@@ -99,6 +99,7 @@ dependencies {
implementation 'com.mpatric:mp3agic:0.9.1'
implementation 'commons-io:commons-io:2.22.0'
implementation 'eu.hansolo:charts:1.0.5'
+ implementation 'io.github.dsheirer:jhydrasdr:1.1.3' //HydraSDR tuners - GitHub release, see settings.gradle
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
implementation 'io.github.dsheirer:usb4java-native-libraries:1.3.3' //OSX & Windows aarch64 native libs
implementation 'io.github.dsheirer:usb4java:1.3.5' //Fork with Loader support for OSX Multi-Version
diff --git a/settings.gradle b/settings.gradle
index ad996325..e691aca6 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -8,5 +8,13 @@ dependencyResolutionManagement {
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
+
+ //jHydraSDR is published as a GitHub release, with the native libraries for all platforms in the jar
+ ivy {
+ url = 'https://github.com/DSheirer/jHydraSDR/releases/download'
+ patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
+ metadataSources { artifact() }
+ content { includeModule 'io.github.dsheirer', 'jhydrasdr' }
+ }
}
}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIterator.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIterator.java
deleted file mode 100644
index 8bba8e5a..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIterator.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * *****************************************************************************
- * Copyright (C) 2014-2025 Dennis Sheirer
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- * ****************************************************************************
- */
-
-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 either ComplexSamples or InterleavedComplexSamples
- */
-public abstract class HydraSdrBufferIterator implements Iterator
-{
- 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;
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorScalar.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorScalar.java
deleted file mode 100644
index 2594f107..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorScalar.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- /**
- * 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector128Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector128Bits.java
deleted file mode 100644
index fe2fe1c7..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector128Bits.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector256Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector256Bits.java
deleted file mode 100644
index bf828911..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector256Bits.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector512Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector512Bits.java
deleted file mode 100644
index bfd7e7a1..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector512Bits.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector64Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector64Bits.java
deleted file mode 100644
index 75454525..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrBufferIteratorVector64Bits.java
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorScalar.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorScalar.java
deleted file mode 100644
index c484d1e4..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorScalar.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- /**
- * 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector128Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector128Bits.java
deleted file mode 100644
index 2033a6b7..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector128Bits.java
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector256Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector256Bits.java
deleted file mode 100644
index 742c361b..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector256Bits.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector512Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector512Bits.java
deleted file mode 100644
index 39dc36dc..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector512Bits.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector64Bits.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector64Bits.java
deleted file mode 100644
index 048465b1..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/HydraSdrInterleavedBufferIteratorVector64Bits.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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
-{
- private static final VectorSpecies 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);
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/IHydraSdrSampleConverter.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/IHydraSdrSampleConverter.java
deleted file mode 100644
index 771f559b..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/IHydraSdrSampleConverter.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-package io.github.dsheirer.buffer.hydrasdr;
-
-import java.nio.ByteBuffer;
-
-/**
- * Interface to convert HydraSDR samples from a byte buffer to an array of shorts.
- */
-public interface IHydraSdrSampleConverter
-{
- /**
- * 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);
-
- /**
- * Current DC average of the samples that have been processed thus far.
- */
- float getAverageDc();
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/ScalarPackedSampleConverter.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/ScalarPackedSampleConverter.java
deleted file mode 100644
index 04d40d0b..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/ScalarPackedSampleConverter.java
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-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();
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/ScalarUnpackedSampleConverter.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/ScalarUnpackedSampleConverter.java
deleted file mode 100644
index 6c0fc1fa..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/ScalarUnpackedSampleConverter.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-package io.github.dsheirer.buffer.hydrasdr;
-
-import io.github.dsheirer.buffer.DcCorrectionManager;
-import java.nio.ByteBuffer;
-
-/**
- * Scalar implementation of HydraSDR sample converter for un-packed samples.
- */
-public class ScalarUnpackedSampleConverter 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;
-
- 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 *= HydraSdrBufferIterator.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();
- }
-}
diff --git a/src/main/java/io/github/dsheirer/buffer/hydrasdr/VectorUnpackedSampleConverter.java b/src/main/java/io/github/dsheirer/buffer/hydrasdr/VectorUnpackedSampleConverter.java
deleted file mode 100644
index c0a6cc34..00000000
--- a/src/main/java/io/github/dsheirer/buffer/hydrasdr/VectorUnpackedSampleConverter.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * *****************************************************************************
- * 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
- * ****************************************************************************
- */
-
-package io.github.dsheirer.buffer.hydrasdr;
-
-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;
-
-/**
- * Vector implementation of HydraSDR sample converter for un-packed samples.
- */
-public class VectorUnpackedSampleConverter implements IHydraSdrSampleConverter
-{
- private static final VectorSpecies 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 *= HydraSdrBufferIterator.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();
- }
-}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrDeviceInformation.java b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrDeviceInformation.java
index 21fe36fe..3041fc70 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrDeviceInformation.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrDeviceInformation.java
@@ -1,156 +1,60 @@
package io.github.dsheirer.source.tuner.hydrasdr;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Arrays;
+import io.github.dsheirer.hydrasdr.DeviceInfo;
+/**
+ * HydraSDR device information, formatted for display and for identifying the tuner.
+ */
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";
+ private final DeviceInfo mDeviceInfo;
/**
- * HydraSDR Device Information - provides parsing and access to device
- * information components.
+ * Constructs an instance
+ * @param deviceInfo from the device
*/
- public HydraSdrDeviceInformation()
+ public HydraSdrDeviceInformation(DeviceInfo deviceInfo)
{
+ mDeviceInfo = deviceInfo;
}
- public void setBoardID( int boardID )
+ public String getBoardName()
{
- 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 ) );
- }
+ return mDeviceInfo.boardName();
}
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 );
+ return mDeviceInfo.firmwareVersion().replace("HydraSDR ", "");
}
+ /**
+ * Serial number, formatted as the identifier that tuner configurations are saved under.
+ */
public String getSerialNumber()
{
- return mSerialNumber;
+ long serial = mDeviceInfo.serialNumber();
+ return String.format("%08X-%08X", serial >>> 32, serial & 0xFFFFFFFFL);
}
public String getPartNumber()
{
- return mPartNumber;
+ return String.format("%016X", mDeviceInfo.partId());
}
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() );
+ sb.append("HydraSDR Device Information\n");
+ sb.append("Board: ");
+ sb.append(getBoardName());
+ sb.append("\nPart Number: ");
+ sb.append(getPartNumber());
+ sb.append("\nSerial Number: ");
+ sb.append(getSerialNumber());
+ sb.append("\nFirmware: ");
+ sb.append(getVersion());
return sb.toString();
}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTuner.java b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTuner.java
index 134dbd6b..205da2f9 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTuner.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTuner.java
@@ -22,7 +22,6 @@ import io.github.dsheirer.preference.source.ChannelizerType;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
import io.github.dsheirer.source.tuner.Tuner;
import io.github.dsheirer.source.tuner.TunerClass;
-import io.github.dsheirer.source.tuner.hydrasdr.HydraSdrTunerController.BoardID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -71,7 +70,7 @@ public class HydraSdrTuner extends Tuner
mLog.error("error getting serial number", e);
}
- return BoardID.HYDRASDR_RFONE.getLabel();
+ return "HydraSDR RFOne";
}
@Override
diff --git a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerController.java
index 3b72b25d..f359de8e 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerController.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerController.java
@@ -1,6 +1,6 @@
/*
* *****************************************************************************
- * Copyright (C) 2014-2025 Dennis Sheirer
+ * Copyright (C) 2014-2026 Dennis Sheirer
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,31 +19,42 @@
package io.github.dsheirer.source.tuner.hydrasdr;
-import io.github.dsheirer.buffer.INativeBufferFactory;
-import io.github.dsheirer.buffer.sample.SampleNativeBufferFactory;
+import io.github.dsheirer.buffer.FloatNativeBuffer;
+import io.github.dsheirer.buffer.INativeBuffer;
+import io.github.dsheirer.hydrasdr.DecimationMode;
+import io.github.dsheirer.hydrasdr.GainType;
+import io.github.dsheirer.hydrasdr.HydraSdr;
+import io.github.dsheirer.hydrasdr.HydraSdrDevice;
+import io.github.dsheirer.hydrasdr.HydraSdrException;
+import io.github.dsheirer.hydrasdr.SampleBuffer;
+import io.github.dsheirer.hydrasdr.SampleType;
+import io.github.dsheirer.sample.Listener;
import io.github.dsheirer.source.SourceException;
import io.github.dsheirer.source.tuner.ITunerErrorListener;
+import io.github.dsheirer.source.tuner.TunerController;
import io.github.dsheirer.source.tuner.TunerType;
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
-import io.github.dsheirer.source.tuner.usb.USBTunerController;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
+import io.github.dsheirer.source.tuner.manager.TunerManager;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
-import org.apache.commons.io.EndianUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.usb4java.Context;
+import org.usb4java.Device;
+import org.usb4java.DeviceDescriptor;
+import org.usb4java.DeviceHandle;
+import org.usb4java.DeviceList;
import org.usb4java.LibUsb;
-import org.usb4java.LibUsbException;
-
-import javax.usb.UsbException;
/**
- * HydraSDR tuner controller
+ * HydraSDR tuner controller, implemented with the jHydraSDR library (libhydrasdr).
+ *
+ * The tuner is discovered with LibUsb at a USB bus and port, like all other USB tuners, however libhydrasdr owns the
+ * USB device, its transfers, and the conversion of real samples to complex samples.
*/
-public class HydraSdrTunerController extends USBTunerController
+public class HydraSdrTunerController extends TunerController
{
private final static Logger mLog = LoggerFactory.getLogger(HydraSdrTunerController.class);
public static final Gain LINEARITY_GAIN_DEFAULT = Gain.LINEARITY_14;
@@ -63,16 +74,25 @@ public class HydraSdrTunerController extends USBTunerController
public static final long MAXIMUM_TUNABLE_FREQUENCY_HZ = 1800000000l;
public static final long FREQUENCY_DEFAULT = 101100000;
public static final double USABLE_BANDWIDTH_PERCENT = 0.90;
- private static final long USB_TIMEOUT_MS = 2000l; //milliseconds
- private static final int USB_TRANSFER_BUFFER_SIZE = 262144;
- private static final byte USB_REQUEST_IN = (byte) (LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR | LibUsb.RECIPIENT_DEVICE);
- 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 HydraSdrSampleRate DEFAULT_SAMPLE_RATE = new HydraSdrSampleRate(0, 10000000, "10.00 MHz");
- private SampleNativeBufferFactory mNativeBufferFactory = new SampleNativeBufferFactory();
+ //Lower rates from libhydrasdr don't divide into whole 25 kHz polyphase channels, and the lowest give none at all.
+ private static final long MINIMUM_SAMPLE_RATE = 1250000;
+ //Power-of-2 sample count of an undecimated buffer, used until buffers arrive from the device. libhydrasdr divides
+ //this by the software decimation factor, which is also a power of 2.
+ private static final int UNDECIMATED_BUFFER_SAMPLE_COUNT = 65536;
+ //Complex samples from libhydrasdr are half the amplitude (-6 dB) of the analytic signal for a real input. Restoring
+ //this keeps spectrum levels and squelch thresholds consistent with the other tuners.
+ private static final float SAMPLE_SCALE = 2.0f;
+ private final int mBus;
+ private final String mPortAddress;
+ private HydraSdrDevice mDevice;
private HydraSdrDeviceInformation mDeviceInfo;
private List mSampleRates = new ArrayList<>();
private int mSampleRate = 0;
+ private volatile float mSamplesPerMillisecond = 0.0f;
+ private volatile int mBufferSampleCount = UNDECIMATED_BUFFER_SAMPLE_COUNT;
+ private boolean mStreaming = false;
/**
* Constructs an instance
@@ -82,7 +102,13 @@ public class HydraSdrTunerController extends USBTunerController
*/
public HydraSdrTunerController(int bus, String portAddress, ITunerErrorListener tunerErrorListener)
{
- super(bus, portAddress, MINIMUM_TUNABLE_FREQUENCY_HZ, MAXIMUM_TUNABLE_FREQUENCY_HZ, 0, USABLE_BANDWIDTH_PERCENT, tunerErrorListener);
+ super(tunerErrorListener);
+ mBus = bus;
+ mPortAddress = portAddress;
+ setMinimumFrequency(MINIMUM_TUNABLE_FREQUENCY_HZ);
+ setMaximumFrequency(MAXIMUM_TUNABLE_FREQUENCY_HZ);
+ setMiddleUnusableHalfBandwidth(0);
+ setUsableBandwidthPercentage(USABLE_BANDWIDTH_PERCENT);
}
@Override
@@ -91,77 +117,334 @@ public class HydraSdrTunerController extends USBTunerController
return TunerType.HYDRASDR_R828D;
}
- @Override
- protected INativeBufferFactory getNativeBufferFactory()
- {
- return mNativeBufferFactory;
- }
-
- @Override
- protected int getTransferBufferSize()
- {
- return USB_TRANSFER_BUFFER_SIZE;
- }
-
@Override
public int getBufferSampleCount()
{
- //Each HydraSDR complex sample is 4 bytes, so sample count is buffer size / 4
- return USB_TRANSFER_BUFFER_SIZE / 4;
+ return mBufferSampleCount;
}
@Override
- protected void deviceStart() throws SourceException
+ public void start() throws SourceException
{
- try
- {
- setSamplePacking(false);
- }
- catch(LibUsbException | UsbException | UnsupportedOperationException e)
- {
- mLog.info("Sample packing is not supported by HydraSDR firmware");
- }
+ long serialNumber = readSerialNumber();
+
+ getLock().lock();
try
{
- setReceiverMode(true);
- }
- catch(Exception e)
- {
- mLog.error("Couldn't enable HydraSDR receiver mode", e);
- }
+ mDevice = HydraSdr.open(serialNumber);
+ mDevice.setSampleType(SampleType.FLOAT32_IQ);
+ mDevice.setDecimationMode(DecimationMode.HIGH_DEFINITION);
+ mDeviceInfo = new HydraSdrDeviceInformation(mDevice.deviceInfo());
+ mSampleRates.clear();
- setFrequency(FREQUENCY_DEFAULT);
+ for(long rate : mDevice.sampleRates())
+ {
+ if(rate >= MINIMUM_SAMPLE_RATE)
+ {
+ mSampleRates.add(new HydraSdrSampleRate(mSampleRates.size(), (int)rate, formatSampleRate((int)rate)));
+ }
+ }
- try
- {
- determineAvailableSampleRates();
- }
- catch(LibUsbException | UsbException e)
- {
- mLog.error("Error identifying available samples rates", e);
- }
+ if(mSampleRates.isEmpty())
+ {
+ mSampleRates.add(DEFAULT_SAMPLE_RATE);
+ }
- try
- {
+ setFrequency(FREQUENCY_DEFAULT);
setSampleRate(mSampleRates.get(0));
}
- catch(IllegalArgumentException | LibUsbException | UsbException e)
+ catch(HydraSdrException | SourceException | LinkageError e)
{
- mLog.error("Setting sample rate is not supported by firmware", e);
+ closeDevice(mDevice);
+ mDevice = null;
+ throw new SourceException("Can't start HydraSDR tuner - " + e.getMessage(), e);
+ }
+ finally
+ {
+ getLock().unlock();
}
}
@Override
- protected void deviceStop()
+ public void stop()
+ {
+ HydraSdrDevice device;
+
+ getLock().lock();
+
+ try
+ {
+ device = mDevice;
+ mDevice = null;
+ mStreaming = false;
+ }
+ finally
+ {
+ getLock().unlock();
+ }
+
+ mNativeBufferBroadcaster.clear();
+ closeDevice(device);
+ }
+
+ /**
+ * Closes the device, which also stops streaming and turns off the bias-T.
+ * @param device to close, or null
+ */
+ private static void closeDevice(HydraSdrDevice device)
+ {
+ if(device != null)
+ {
+ try
+ {
+ device.close();
+ }
+ catch(HydraSdrException | RuntimeException e)
+ {
+ mLog.error("Error closing HydraSDR device", e);
+ }
+ }
+ }
+
+ /**
+ * Reads the serial number of the device at this controller's USB bus and port, since libhydrasdr opens a specific
+ * device by serial number. The device is closed again before returning so that libhydrasdr can open it.
+ * @return serial number
+ * @throws SourceException if the device isn't found or can't be accessed.
+ */
+ private long readSerialNumber() throws SourceException
+ {
+ Context context = new Context();
+ int status = LibUsb.init(context);
+
+ if(status != LibUsb.SUCCESS)
+ {
+ throw new SourceException("Can't initialize libusb library - " + LibUsb.errorName(status));
+ }
+
+ try
+ {
+ DeviceList deviceList = new DeviceList();
+ int count = LibUsb.getDeviceList(context, deviceList);
+
+ if(count < 0)
+ {
+ throw new SourceException("Can't get the USB device list - " + LibUsb.errorName(count));
+ }
+
+ try
+ {
+ for(Device device : deviceList)
+ {
+ if(LibUsb.getBusNumber(device) == mBus && LibUsb.getPortNumber(device) > 0 &&
+ mPortAddress != null && mPortAddress.equals(TunerManager.getPortAddress(device)))
+ {
+ return readSerialNumber(device);
+ }
+ }
+ }
+ finally
+ {
+ LibUsb.freeDeviceList(deviceList, true);
+ }
+ }
+ finally
+ {
+ LibUsb.exit(context);
+ }
+
+ throw new SourceException("Couldn't find USB device at bus [" + mBus + "] port [" + mPortAddress + "]");
+ }
+
+ /**
+ * Reads the serial number from the device's USB serial string, formatted as: HYDRASDR SN:0123456789ABCDEF
+ * @param device to read
+ * @return serial number
+ * @throws SourceException if the device can't be opened or the serial string isn't valid.
+ */
+ private static long readSerialNumber(Device device) throws SourceException
+ {
+ DeviceDescriptor descriptor = new DeviceDescriptor();
+ int status = LibUsb.getDeviceDescriptor(device, descriptor);
+
+ if(status != LibUsb.SUCCESS)
+ {
+ throw new SourceException("Can't obtain tuner's device descriptor - " + LibUsb.errorName(status));
+ }
+
+ DeviceHandle handle = new DeviceHandle();
+ status = LibUsb.open(device, handle);
+
+ if(status == LibUsb.ERROR_ACCESS)
+ {
+ mLog.error("Access to USB tuner denied - (windows) reinstall zadig driver or (linux) blacklist driver and/or check udev rules");
+ throw new SourceException("access denied - if using linux, blacklist the default driver and/or install udev rules");
+ }
+ else if(status != LibUsb.SUCCESS)
+ {
+ mLog.error("Can't open USB tuner - check driver or Linux udev rules");
+ throw new SourceException("Can't open USB tuner - reinstall driver? - " + LibUsb.errorName(status));
+ }
+
+ String serial;
+
+ try
+ {
+ serial = LibUsb.getStringDescriptor(handle, descriptor.iSerialNumber());
+ }
+ finally
+ {
+ LibUsb.close(handle);
+ }
+
+ try
+ {
+ return Long.parseUnsignedLong(serial.substring(serial.lastIndexOf(':') + 1), 16);
+ }
+ catch(NullPointerException | NumberFormatException e)
+ {
+ throw new SourceException("Can't read HydraSDR serial number from USB serial string [" + serial + "]", e);
+ }
+ }
+
+ /**
+ * Operation that requires the device.
+ */
+ private interface DeviceOperation
+ {
+ void execute(HydraSdrDevice device) throws HydraSdrException;
+ }
+
+ /**
+ * Executes the operation under the controller lock so that the device can't be closed while it is in use. The
+ * operation is ignored when the device isn't open, which allows for calls that arrive during tuner shutdown.
+ * @param operation to execute
+ * @throws SourceException if the operation fails
+ */
+ private void execute(DeviceOperation operation) throws SourceException
+ {
+ getLock().lock();
+
+ try
+ {
+ if(mDevice != null)
+ {
+ operation.execute(mDevice);
+ }
+ }
+ catch(HydraSdrException e)
+ {
+ throw new SourceException(e.getMessage(), e);
+ }
+ finally
+ {
+ getLock().unlock();
+ }
+ }
+
+ /**
+ * Receives converted samples on the libhydrasdr thread and dispatches them to the buffer listeners. Exceptions
+ * are contained here since an exception reaching the library would stop the sample stream.
+ * @param buffer from the library, which is only valid until this method returns.
+ * @return true to continue streaming
+ */
+ private boolean receive(SampleBuffer buffer)
{
try
{
- setReceiverMode(false);
+ mBufferSampleCount = buffer.sampleCount();
+ float[] samples = buffer.copyFloats();
+
+ for(int x = 0; x < samples.length; x++)
+ {
+ samples[x] *= SAMPLE_SCALE;
+ }
+
+ broadcast(new FloatNativeBuffer(samples, System.currentTimeMillis(), mSamplesPerMillisecond));
}
- catch(Exception e)
+ catch(RuntimeException e)
{
- mLog.error("Couldn't enable HydraSDR receiver mode", e);
+ mLog.error("Error dispatching HydraSDR sample buffer", e);
+ }
+
+ return true;
+ }
+
+ /**
+ * Starts the sample stream if it's not already started.
+ */
+ private void startStreaming() throws SourceException
+ {
+ if(!mStreaming)
+ {
+ execute(device -> device.startRx(this::receive));
+ mStreaming = mDevice != null;
+ }
+ }
+
+ /**
+ * Stops the sample stream if it's started.
+ */
+ private void stopStreaming() throws SourceException
+ {
+ if(mStreaming)
+ {
+ mStreaming = false;
+ execute(HydraSdrDevice::stopRx);
+ }
+ }
+
+ /**
+ * Adds the IQ buffer listener and automatically starts streaming, if not already started.
+ */
+ @Override
+ public void addBufferListener(Listener listener)
+ {
+ getLock().lock();
+
+ try
+ {
+ if(mDevice != null)
+ {
+ super.addBufferListener(listener);
+ startStreaming();
+ }
+ }
+ catch(SourceException se)
+ {
+ mLog.error("Error starting streaming on HydraSDR tuner", se);
+ }
+ finally
+ {
+ getLock().unlock();
+ }
+ }
+
+ /**
+ * Removes the IQ buffer listener and stops streaming if there are no more listeners.
+ */
+ @Override
+ public void removeBufferListener(Listener listener)
+ {
+ getLock().lock();
+
+ try
+ {
+ super.removeBufferListener(listener);
+
+ if(!hasBufferListeners())
+ {
+ stopStreaming();
+ }
+ }
+ catch(SourceException se)
+ {
+ mLog.error("Error stopping streaming on HydraSDR tuner", se);
+ }
+ finally
+ {
+ getLock().unlock();
}
}
@@ -173,29 +456,8 @@ public class HydraSdrTunerController extends USBTunerController
if(tunerConfiguration instanceof HydraSdrTunerConfiguration config)
{
- int sampleRate = config.getSampleRate();
- HydraSdrSampleRate rate = getSampleRate(sampleRate);
-
- if(rate == null)
- {
- if(!mSampleRates.isEmpty())
- {
- rate = mSampleRates.get(0);
- }
- else
- {
- rate = DEFAULT_SAMPLE_RATE;
- }
- }
-
- try
- {
- setSampleRate(rate);
- }
- catch(UsbException e)
- {
- throw new SourceException("Couldn't set sample rate [" + rate.toString() + "]", e);
- }
+ HydraSdrSampleRate rate = getSampleRate(config.getSampleRate());
+ setSampleRate(rate != null ? rate : mSampleRates.isEmpty() ? DEFAULT_SAMPLE_RATE : mSampleRates.get(0));
try
{
@@ -231,24 +493,11 @@ public class HydraSdrTunerController extends USBTunerController
}
@Override
- public synchronized void setTunedFrequency(long frequency) throws SourceException
+ public void setTunedFrequency(long frequency) throws SourceException
{
if(MINIMUM_TUNABLE_FREQUENCY_HZ <= frequency && frequency <= MAXIMUM_TUNABLE_FREQUENCY_HZ)
{
- ByteBuffer buffer = ByteBuffer.allocateDirect(4);
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.putInt((int) frequency);
- buffer.rewind();
-
- try
- {
- write(Command.SET_FREQUENCY, 0, 0, buffer);
- }
- catch(UsbException e)
- {
- mLog.error("error setting frequency [" + frequency + "]", e);
- throw new SourceException("error setting frequency [" + frequency + "]", e);
- }
+ execute(device -> device.setFrequency(frequency));
}
else
{
@@ -263,52 +512,71 @@ public class HydraSdrTunerController extends USBTunerController
}
/**
- * Sets the sample rate to the rate specified by the index value in the available sample rates map
+ * Sets the sample rate. The sample stream is stopped for the change, because libhydrasdr replaces the sample
+ * converter that its streaming thread is using.
*
- * @param rate to a sample rate in the available samples rates map.
- * @throws IllegalArgumentException if index is not a valid rate index
- * @throws LibUsbException if there was a read error or if this operation is not supported by the current firmware
- * @throws UsbException if there was a USB error
+ * @param rate to set
+ * @throws SourceException if the rate can't be set
*/
- public void setSampleRate(HydraSdrSampleRate rate) throws LibUsbException, UsbException, SourceException
+ public void setSampleRate(HydraSdrSampleRate rate) throws SourceException
{
- getNativeBufferFactory().setSamplesPerMillisecond((float)rate.getRate() / 1000.0f);
- if(rate.getRate() != mSampleRate)
+ getLock().lock();
+
+ try
{
- int result = readByte(Command.SET_SAMPLE_RATE, 0, rate.getIndex(), true);
+ mSamplesPerMillisecond = (float)rate.getRate() / 1000.0f;
- if(result != 1)
+ if(rate.getRate() != mSampleRate)
{
- throw new UsbException("Error setting sample rate [" + rate + "] rate - return value [" + result + "]");
- }
- else
- {
- mSampleRate = rate.getRate();
- mFrequencyController.setSampleRate(mSampleRate);
+ boolean streaming = mStreaming;
+ stopStreaming();
- // Update the Usable Bandwidth Percentage, as the usable range varies with the sample rate.
- switch(mSampleRate)
+ try
{
- default:
- case 10000000:
- super.setUsableBandwidthPercentage(0.90);
- break;
- case 6000000:
- super.setUsableBandwidthPercentage(0.83);
- break;
- case 3000000:
- super.setUsableBandwidthPercentage(0.66);
- break;
- case 2500000:
- super.setUsableBandwidthPercentage(0.60);
- break;
+ execute(device ->
+ {
+ device.setSampleRate(rate.getRate());
+ mBufferSampleCount = UNDECIMATED_BUFFER_SAMPLE_COUNT /
+ (int)device.deviceInfo().current().decimationFactor();
+ });
+ mSampleRate = rate.getRate();
+ mFrequencyController.setSampleRate(mSampleRate);
+
+ // Update the Usable Bandwidth Percentage, as the usable range varies with the sample rate.
+ switch(mSampleRate)
+ {
+ default:
+ case 10000000:
+ super.setUsableBandwidthPercentage(0.90);
+ break;
+ case 6000000:
+ super.setUsableBandwidthPercentage(0.83);
+ break;
+ case 3000000:
+ super.setUsableBandwidthPercentage(0.66);
+ break;
+ case 2500000:
+ super.setUsableBandwidthPercentage(0.90);
+ break;
+ }
+ }
+ finally
+ {
+ if(streaming)
+ {
+ startStreaming();
+ }
}
}
}
+ finally
+ {
+ getLock().unlock();
+ }
}
/**
- * Returns a list of sample rates supported by the firmware version
+ * Returns a list of sample rates supported by the device
*/
public List getSampleRates()
{
@@ -342,62 +610,29 @@ public class HydraSdrTunerController extends USBTunerController
return null;
}
- /**
- * Enables/Disables sample packing to allow two 12-bit samples to be packed into 3 bytes (enabled) or 4 bytes (disabled).
- *
- * @param enabled true or false
- * @throws UsbException if sample packing is not supported by the current device firmware or if there were usb
- * communication issues
- */
- public void setSamplePacking(boolean enabled) throws LibUsbException, UsbException
- {
- int result = readByte(Command.SET_PACKING, 0, (enabled ? 1 : 0), true);
-
- if(result != 1)
- {
- throw new UsbException("Couldnt set sample packing enabled: " + enabled);
- }
-
- /* If we didn't throw an exception above, then update the sample adapter
- * to process samples accordingly */
- mNativeBufferFactory.setSamplePacking(enabled);
- }
-
/**
* Enables/disables the mixer automatic gain setting
*
- * @param enabled
- * @throws LibUsbException on unsuccessful read operation
- * @throws UsbException on USB error
+ * @param enabled true to enable
+ * @throws SourceException on error
*/
- public synchronized void setMixerAGC(boolean enabled) throws LibUsbException, UsbException
+ public void setMixerAGC(boolean enabled) throws SourceException
{
- int result = readByte(Command.SET_MIXER_AGC, 0, (enabled ? 1 : 0), true);
-
- if(result != LibUsb.SUCCESS)
- {
- throw new UsbException("Couldnt set mixer AGC enabled: " + enabled);
- }
+ execute(device -> device.setGain(GainType.MIXER_AGC, enabled ? 1 : 0));
}
/**
* Enables/disables the low noise amplifier automatic gain setting
*
- * @param enabled
- * @throws LibUsbException on unsuccessful read operation
- * @throws UsbException on USB error
+ * @param enabled true to enable
+ * @throws SourceException on error
*/
- public synchronized void setLNAAGC(boolean enabled) throws LibUsbException, UsbException
+ public void setLNAAGC(boolean enabled) throws SourceException
{
- int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true);
-
- if(result != LibUsb.SUCCESS)
- {
- throw new UsbException("Couldn't set LNA AGC enabled: " + enabled);
- }
+ execute(device -> device.setGain(GainType.LNA_AGC, enabled ? 1 : 0));
}
- public void setGain(Gain gain) throws UsbException
+ public void setGain(Gain gain) throws SourceException
{
if(gain != Gain.CUSTOM)
{
@@ -413,20 +648,14 @@ public class HydraSdrTunerController extends USBTunerController
* Sets LNA gain
*
* @param gain - value within range of LNA_GAIN_MIN to LNA_GAIN_MAX
- * @throws LibUsbException on error in java USB wrapper
- * @throws UsbException on error in USB transfer
+ * @throws SourceException on error
* @throws IllegalArgumentException if gain value is invalid
*/
- public synchronized void setLNAGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException
+ public void setLNAGain(int gain) throws SourceException, IllegalArgumentException
{
if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX)
{
- int result = readByte(Command.SET_LNA_GAIN, 0, gain, true);
-
- if(result != LibUsb.SUCCESS)
- {
- throw new UsbException("Couldnt set LNA gain to: " + gain);
- }
+ execute(device -> device.setGain(GainType.LNA, gain));
}
else
{
@@ -439,20 +668,14 @@ public class HydraSdrTunerController extends USBTunerController
* Sets Mixer gain
*
* @param gain - value within range of MIXER_GAIN_MIN to MIXER_GAIN_MAX
- * @throws LibUsbException on error in java USB wrapper
- * @throws UsbException on error in USB transfer
+ * @throws SourceException on error
* @throws IllegalArgumentException if gain value is invalid
*/
- public void setMixerGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException
+ public void setMixerGain(int gain) throws SourceException, IllegalArgumentException
{
if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX)
{
- int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true);
-
- if(result != LibUsb.SUCCESS)
- {
- throw new UsbException("Couldnt set mixer gain to: " + gain);
- }
+ execute(device -> device.setGain(GainType.MIXER, gain));
}
else
{
@@ -464,21 +687,15 @@ public class HydraSdrTunerController extends USBTunerController
/**
* Sets IF (VGA) gain
*
- * @param gain - value within range of VGA_GAIN_MIN to VGA_GAIN_MAX
- * @throws LibUsbException on error in java USB wrapper
- * @throws UsbException on error in USB transfer
+ * @param gain - value within range of IF_GAIN_MIN to IF_GAIN_MAX
+ * @throws SourceException on error
* @throws IllegalArgumentException if gain value is invalid
*/
- public void setIFGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException
+ public void setIFGain(int gain) throws SourceException, IllegalArgumentException
{
if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX)
{
- int result = readByte(Command.SET_VGA_GAIN, 0, gain, true);
-
- if(result != LibUsb.SUCCESS)
- {
- throw new UsbException("Couldnt set VGA gain to: " + gain);
- }
+ execute(device -> device.setGain(GainType.VGA, gain));
}
else
{
@@ -487,64 +704,15 @@ public class HydraSdrTunerController extends USBTunerController
}
}
- public void setReceiverMode(boolean enabled) throws LibUsbException, UsbException
- {
- //Empty buffer to throw away
- ByteBuffer buffer = ByteBuffer.allocateDirect(0);
- write(Command.RECEIVER_MODE, (enabled ? 1 : 0), 0, buffer);
- }
-
/**
* Enables/disables the Bias-T (RF bias tee) power output for active antennas
*
* @param enabled true to enable Bias-T, false to disable
- * @throws LibUsbException on unsuccessful write operation
- * @throws UsbException on USB error
+ * @throws SourceException on error
*/
- public synchronized void setBiasT(boolean enabled) throws LibUsbException, UsbException
+ public void setBiasT(boolean enabled) throws SourceException
{
- ByteBuffer buffer = ByteBuffer.allocateDirect(0);
- write(Command.SET_RF_BIAS_COMMAND, 0, (enabled ? 1 : 0), buffer);
- }
-
- /**
- * Queries the device for available sample rates. Will always provide at
- * least the default 10 MHz sample rate.
- */
- private void determineAvailableSampleRates() throws LibUsbException, UsbException
- {
- mSampleRates.clear();
-
- //Get a count of available sample rates. If we get an exception, then
- //we're using an older firmware revision and only the default 10 MHz
- //rate is supported
- try
- {
- byte[] rawCount = readArray(Command.GET_SAMPLE_RATES, 0, 0, 4);
-
- if(rawCount != null)
- {
- int count = EndianUtils.readSwappedInteger(rawCount, 0);
-
- byte[] rawRates = readArray(Command.GET_SAMPLE_RATES, 0,
- count, (count * 4));
-
- for(int x = 0; x < count; x++)
- {
- int rate = EndianUtils.readSwappedInteger(rawRates, (x * 4));
- mSampleRates.add(new HydraSdrSampleRate(x, rate, formatSampleRate(rate)));
- }
- }
- }
- catch(LibUsbException e)
- {
- //Press on, nothing else to do here ..
- }
-
- if(mSampleRates.isEmpty())
- {
- mSampleRates.add(DEFAULT_SAMPLE_RATE);
- }
+ execute(device -> device.setBiasTee(enabled));
}
/**
@@ -556,164 +724,13 @@ public class HydraSdrTunerController extends USBTunerController
}
/**
- * Device information
+ * Device information, available once the tuner has been started.
*/
public HydraSdrDeviceInformation getDeviceInfo()
{
- //Lazy initialization
- if(mDeviceInfo == null)
- {
- readDeviceInfo();
- }
-
return mDeviceInfo;
}
- /**
- * Reads version information from the device and populates the info object
- */
- private void readDeviceInfo()
- {
- if(mDeviceInfo == null)
- {
- mDeviceInfo = new HydraSdrDeviceInformation();
- }
-
- /* Board ID */
- try
- {
- int boardID = readByte(Command.BOARD_ID_READ, 0, 0, true);
- mDeviceInfo.setBoardID(boardID);
- }
- catch(LibUsbException | UsbException e)
- {
- mLog.error("Error reading HydraSDR board ID", e);
- }
-
- /* Version String */
- try
- {
- byte[] version = readArray(Command.VERSION_STRING_READ, 0, 0, 127);
- mDeviceInfo.setVersion(version);
- }
- catch(LibUsbException | UsbException e)
- {
- mLog.error("Error reading HydraSDR version string", e);
- }
-
- /* Part ID and Serial Number */
- try
- {
- //Read 6 x 32-bit integers = 24 bytes
- byte[] serial = readArray(Command.BOARD_PART_ID_SERIAL_NUMBER_READ, 0, 0, 24);
- mDeviceInfo.setPartAndSerialNumber(serial);
- }
- catch(LibUsbException | UsbException e)
- {
- mLog.error("Error reading HydraSDR version string", e);
- }
- }
-
- /**
- * Reads a single byte value from the device.
- *
- * @param command - HydraSDR command
- * @param value - value field for usb setup packet
- * @param index - index field for usb setup packet
- * @return - byte value as an integer
- * @throws LibUsbException if the operation is unsuccesful
- * @throws UsbException on any usb errors
- */
- private int readByte(Command command, int value, int index, boolean signed) throws LibUsbException, UsbException
- {
- if(isRunning())
- {
- ByteBuffer buffer = ByteBuffer.allocateDirect(1);
-
- int transferred = LibUsb.controlTransfer(getDeviceHandle(), USB_REQUEST_IN, command.getValue(),
- (short) value, (short) index, buffer, USB_TIMEOUT_MS);
-
- if(transferred < 0)
- {
- throw new LibUsbException("read error", transferred);
- }
-
- byte result = buffer.get(0);
-
- if(signed)
- {
- return (result & 0xFF);
- }
- else
- {
- return result;
- }
- }
-
- return 0;
- }
-
- /**
- * Reads a multi-byte value from the device
- *
- * @param command - HydraSDR command
- * @param value - usb packet value
- * @param index - usb packet index
- * @param length - number of bytes to read
- * @return - bytes read from the device
- * @throws LibUsbException if quantity of bytes read doesn't equal the requested number of bytes
- * @throws UsbException on error communicating with the device
- */
- private byte[] readArray(Command command, int value, int index, int length) throws LibUsbException, UsbException
- {
- if(isRunning())
- {
- ByteBuffer buffer = ByteBuffer.allocateDirect(length);
-
- int transferred = LibUsb.controlTransfer(getDeviceHandle(), USB_REQUEST_IN, command.getValue(), (short) value,
- (short) index, buffer, USB_TIMEOUT_MS);
-
- if(transferred < 0)
- {
- throw new LibUsbException("read error", transferred);
- }
-
- byte[] results = new byte[transferred];
- buffer.get(results);
- return results;
- }
-
- return new byte[0];
- }
-
- /**
- * Writes the buffer contents to the device
- *
- * @param command - HydraSDR command
- * @param value - usb packet value
- * @param index - usb packet index
- * @param buffer - data to write to the device
- * @throws UsbException on error
- */
- public void write(Command command, int value, int index, ByteBuffer buffer) throws UsbException
- {
- if(isRunning())
- {
- int transferred = LibUsb.controlTransfer(getDeviceHandle(), USB_REQUEST_OUT, command.getValue(),
- (short) value, (short) index, buffer, USB_TIMEOUT_MS);
-
- if(transferred < 0)
- {
- throw new LibUsbException("error writing byte buffer", transferred);
- }
- else if(transferred != buffer.capacity())
- {
- throw new LibUsbException("transferred bytes [" + transferred + "] is not what was expected [" +
- buffer.capacity() + "]", transferred);
- }
- }
- }
-
public enum GainMode
{
LINEARITY,
@@ -856,199 +873,4 @@ public class HydraSdrTunerController extends USBTunerController
return EnumSet.range(SENSITIVITY_1, SENSITIVITY_22);
}
}
-
- /**
- * HydraSDR Board Identifier
- */
- public enum BoardID
- {
- HYDRASDR_PROTO(0, "HydraSDR Proto"),
- HYDRASDR_RFONE(1, "HydraSDR RF-One"),
- UNKNOWN(-1, "Unknown");
-
- private int mValue;
- private String mLabel;
-
- private BoardID(int value, String label)
- {
- mValue = value;
- mLabel = label;
- }
-
- public int getValue()
- {
- return mValue;
- }
-
- public String getLabel()
- {
- return mLabel;
- }
-
- public static BoardID fromValue(int value)
- {
- if(value == 0)
- {
- return HYDRASDR_PROTO;
- }
- else if(value == 1)
- {
- return HYDRASDR_RFONE;
- }
-
- return UNKNOWN;
- }
- }
-
- /**
- * HydraSDR Commands
- */
- public enum Command
- {
- INVALID(0),
- RECEIVER_MODE(1),
- SI5351C_WRITE(2),
- SI5351C_READ(3),
- R828D_WRITE(4),
- R828D_READ(5),
- SPIFLASH_ERASE(6),
- SPIFLASH_WRITE(7),
- SPIFLASH_READ(8),
- BOARD_ID_READ(9),
- VERSION_STRING_READ(10),
- BOARD_PART_ID_SERIAL_NUMBER_READ(11),
- SET_SAMPLE_RATE(12),
- SET_FREQUENCY(13),
- SET_LNA_GAIN(14),
- SET_MIXER_GAIN(15),
- SET_VGA_GAIN(16),
- SET_LNA_AGC(17),
- SET_MIXER_AGC(18),
- MS_VENDOR_COMMAND(19),
- SET_RF_BIAS_COMMAND(20),
- GPIO_WRITE(21),
- GPIO_READ(22),
- GPIO_DIR__WRITE(23),
- GPIO_DIR_READ(24),
- GET_SAMPLE_RATES(25),
- SET_PACKING(26);
-
- private int mValue;
-
- Command(int value)
- {
- mValue = value;
- }
-
- public byte getValue()
- {
- return (byte) mValue;
- }
-
- public static Command fromValue(int value)
- {
- if(0 <= value && value <= 25)
- {
- return Command.values()[value];
- }
-
- return INVALID;
- }
- }
-
- public enum ReceiverMode
- {
- OFF(0),
- ON(1);
-
- private int mValue;
-
- private ReceiverMode(int value)
- {
- mValue = value;
- }
-
- public int getValue()
- {
- return mValue;
- }
- }
-
- /**
- * General Purpose Input/Output Ports (accessible on the HydraSDR board)
- */
- public enum GPIOPort
- {
- PORT_0(0),
- PORT_1(1),
- PORT_2(2),
- PORT_3(3),
- PORT_4(4),
- PORT_5(5),
- PORT_6(6),
- PORT_7(7);
-
- private int mValue;
-
- private GPIOPort(int value)
- {
- mValue = value;
- }
-
- public int getValue()
- {
- return mValue;
- }
- }
-
- /**
- * General Purpose Input/Output Pins (accessible on the HydraSDR board)
- */
- public enum GPIOPin
- {
- PIN_0(0),
- PIN_1(1),
- PIN_2(2),
- PIN_3(3),
- PIN_4(4),
- PIN_5(5),
- PIN_6(6),
- PIN_7(7),
- PIN_8(8),
- PIN_9(9),
- PIN_10(10),
- PIN_11(11),
- PIN_12(12),
- PIN_13(13),
- PIN_14(14),
- PIN_15(15),
- PIN_16(16),
- PIN_17(17),
- PIN_18(18),
- PIN_19(19),
- PIN_20(20),
- PIN_21(21),
- PIN_22(22),
- PIN_23(23),
- PIN_24(24),
- PIN_25(25),
- PIN_26(26),
- PIN_27(27),
- PIN_28(28),
- PIN_29(29),
- PIN_30(30),
- PIN_31(31);
-
- private int mValue;
-
- GPIOPin(int value)
- {
- mValue = value;
- }
-
- public int getValue()
- {
- return mValue;
- }
- }
}
diff --git a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerEditor.java
index cbde8c5c..fa1cd2b1 100644
--- a/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerEditor.java
+++ b/src/main/java/io/github/dsheirer/source/tuner/hydrasdr/HydraSdrTunerEditor.java
@@ -722,16 +722,15 @@ public class HydraSdrTunerEditor extends TunerEditor");
sb.append("Firmware: ");
- String[] firmware = info.getVersion().split(" ");
- sb.append(firmware.length > 1 ? firmware[0] : info.getVersion());
+ sb.append(info.getVersion());
sb.append("
");
sb.append("Part: ");
sb.append(info.getPartNumber());
sb.append("
");
- sb.append("Board ID: ");
- sb.append(info.getBoardID().getLabel());
+ sb.append("Board: ");
+ sb.append(info.getBoardName());
sb.append("
");
return sb.toString();