mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-09 15:17:24 -05:00
Co-authored-by: denny <dennis.sheirer@gmail.com>
This commit is contained in:
@@ -124,8 +124,9 @@ 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:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jairspy:1.0.12' //Airspy tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jairspyhf:1.8.1' //Airspy HF+ tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jbladerf:2.6.0' //bladeRF tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jhydrasdr:1.1.4' //HydraSDR tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:jrtl:1.4.0.1' //RTL-SDR tuners - GitHub release, see settings.gradle
|
||||
implementation 'io.github.dsheirer:radio-reference-api:18.0.0'
|
||||
|
||||
@@ -17,6 +17,14 @@ dependencyResolutionManagement {
|
||||
content { includeModule 'io.github.dsheirer', 'jairspy' }
|
||||
}
|
||||
|
||||
//jAirspyHF is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||
ivy {
|
||||
url = 'https://github.com/DSheirer/jAirspyHF/releases/download'
|
||||
patternLayout { artifact 'v[revision]/[artifact]-[revision].[ext]' }
|
||||
metadataSources { artifact() }
|
||||
content { includeModule 'io.github.dsheirer', 'jairspyhf' }
|
||||
}
|
||||
|
||||
//jRTL is published as a GitHub release, with the native libraries for all platforms in the jar
|
||||
ivy {
|
||||
url = 'https://github.com/DSheirer/jRTL/releases/download'
|
||||
|
||||
@@ -1,132 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBuffer;
|
||||
import io.github.dsheirer.sample.complex.ComplexSamples;
|
||||
import io.github.dsheirer.sample.complex.InterleavedComplexSamples;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Native buffer implementation for Airspy HF+ & Discovery tuners.
|
||||
*/
|
||||
public class AirspyHfNativeBuffer extends AbstractNativeBuffer
|
||||
{
|
||||
public static final float SCALE = 1.0f / 32768.0f;
|
||||
private short[] mInterleavedSamples;
|
||||
private float mAverageDc;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
* @param timestamp of the first sample of the buffer
|
||||
* @param interleavedSamples pairs of 16-bit complex samples.
|
||||
*/
|
||||
public AirspyHfNativeBuffer(long timestamp, float samplesPerMillisecond, float averageDc, short[] interleavedSamples)
|
||||
{
|
||||
super(timestamp, samplesPerMillisecond);
|
||||
mInterleavedSamples = interleavedSamples;
|
||||
mAverageDc = averageDc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<ComplexSamples> iterator()
|
||||
{
|
||||
return new IteratorComplexSamples();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<InterleavedComplexSamples> iteratorInterleaved()
|
||||
{
|
||||
return new IteratorInterleaved();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int sampleCount()
|
||||
{
|
||||
return mInterleavedSamples.length / 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scalar implementation of complex samples buffer iterator
|
||||
*/
|
||||
public class IteratorComplexSamples implements Iterator<ComplexSamples>
|
||||
{
|
||||
private boolean mHasNext = true;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return mHasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComplexSamples next()
|
||||
{
|
||||
int length = mInterleavedSamples.length / 2;
|
||||
float[] i = new float[length];
|
||||
float[] q = new float[length];
|
||||
|
||||
int index = 0;
|
||||
|
||||
for(int x = 0; x < mInterleavedSamples.length; x += 2)
|
||||
{
|
||||
index = x / 2;
|
||||
//Native ordering is in Q, I, Q, I ... reverse it to I, Q, I, Q
|
||||
i[index] = (mInterleavedSamples[x + 1] * SCALE) - mAverageDc;
|
||||
q[index] = (mInterleavedSamples[x] * SCALE) - mAverageDc;
|
||||
}
|
||||
|
||||
mHasNext = false;
|
||||
|
||||
return new ComplexSamples(i, q, getTimestamp());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scalar implementation of interleaved sample buffer iterator.
|
||||
*/
|
||||
public class IteratorInterleaved implements Iterator<InterleavedComplexSamples>
|
||||
{
|
||||
private boolean mHasNext = true;
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return mHasNext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterleavedComplexSamples next()
|
||||
{
|
||||
float[] samples = new float[mInterleavedSamples.length];
|
||||
|
||||
for(int x = 0; x < mInterleavedSamples.length; x += 2)
|
||||
{
|
||||
//Native ordering is in Q, I, Q, I ... reverse it to I, Q, I, Q
|
||||
samples[x] = (mInterleavedSamples[x + 1] * SCALE) - mAverageDc;
|
||||
samples[x + 1] = (mInterleavedSamples[x] * SCALE) - mAverageDc;
|
||||
}
|
||||
|
||||
mHasNext = false;
|
||||
|
||||
return new InterleavedComplexSamples(samples, getTimestamp());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2025 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.buffer.airspy;
|
||||
|
||||
import io.github.dsheirer.buffer.AbstractNativeBufferFactory;
|
||||
import io.github.dsheirer.buffer.DcCorrectionManager;
|
||||
import io.github.dsheirer.buffer.INativeBuffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.ShortBuffer;
|
||||
|
||||
/**
|
||||
* Airspy HF+ native buffer factory.
|
||||
*
|
||||
* Note: the tuner provides 1024 samples in each transfer where samples are 16-bit interleaved complex.
|
||||
*/
|
||||
public class AirspyHfNativeBufferFactory extends AbstractNativeBufferFactory
|
||||
{
|
||||
private DcCorrectionManager mDcCorrectionManager = new DcCorrectionManager();
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*/
|
||||
public AirspyHfNativeBufferFactory()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the samples byte buffer into a native buffer and calculates DC offset
|
||||
* @param samples byte array copied from native memory
|
||||
* @param timestamp of the samples
|
||||
* @return constructed native buffer
|
||||
*/
|
||||
@Override
|
||||
public INativeBuffer getBuffer(ByteBuffer samples, long timestamp)
|
||||
{
|
||||
ShortBuffer shortBuffer = samples.order(ByteOrder.LITTLE_ENDIAN).asShortBuffer();
|
||||
short[] converted = new short[shortBuffer.capacity()];
|
||||
shortBuffer.get(converted);
|
||||
|
||||
if(mDcCorrectionManager.shouldCalculateDc())
|
||||
{
|
||||
calculateDc(converted);
|
||||
}
|
||||
|
||||
return new AirspyHfNativeBuffer(timestamp, getSamplesPerMillisecond(), mDcCorrectionManager.getAverageDc(), converted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the average DC in the sample stream so that it can be subtracted from the samples when the
|
||||
* native buffer is used.
|
||||
* @param samples containing DC offset
|
||||
*/
|
||||
private void calculateDc(short[] samples)
|
||||
{
|
||||
float dcAccumulator = 0;
|
||||
|
||||
for(short sample: samples)
|
||||
{
|
||||
dcAccumulator += sample;
|
||||
}
|
||||
|
||||
dcAccumulator /= samples.length;
|
||||
mDcCorrectionManager.adjust(dcAccumulator * AirspyHfNativeBuffer.SCALE);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2024 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
|
||||
@@ -24,7 +24,6 @@ import io.github.dsheirer.source.SourceException;
|
||||
import io.github.dsheirer.source.tuner.manager.DiscoveredTuner;
|
||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||
import io.github.dsheirer.source.tuner.ui.TunerEditor;
|
||||
import java.io.IOException;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -147,11 +146,9 @@ public class AirspyHfTunerEditor extends TunerEditor<AirspyHfTuner,AirspyHfTuner
|
||||
if(hasTuner())
|
||||
{
|
||||
getTunerIdLabel().setText("Airspy " + getTuner().getController().getBoardId() + " SER# " +
|
||||
getTuner().getController().getSerialNumber());
|
||||
getTuner().getController().getSerialNumber() + " FW " + getTuner().getController().getFirmwareVersion());
|
||||
|
||||
//Permanently disable the sample rates combo and force 912kHz usage - during dev testing, attempts to use
|
||||
//the other sample rates produced inconsistent results.
|
||||
getSampleRateCombo().setEnabled(false);
|
||||
getSampleRateCombo().setEnabled(!getTuner().getTunerController().isLockedSampleRate());
|
||||
getSampleRateCombo().removeAllItems();
|
||||
for(AirspyHfSampleRate sampleRate: getTuner().getController().getAvailableSampleRates())
|
||||
{
|
||||
@@ -162,7 +159,7 @@ public class AirspyHfTunerEditor extends TunerEditor<AirspyHfTuner,AirspyHfTuner
|
||||
getLnaToggleButton().setEnabled(true);
|
||||
getLnaToggleButton().setSelected(getTuner().getController().getLna());
|
||||
getSampleRateCombo().setSelectedItem(getTuner().getController().getCurrentAirspySampleRate());
|
||||
getAttenuationCombo().setEnabled(true);
|
||||
getAttenuationCombo().setEnabled(!getTuner().getController().getAgc());
|
||||
getAttenuationCombo().setSelectedItem(getTuner().getController().getAttenuation());
|
||||
}
|
||||
else
|
||||
@@ -260,6 +257,7 @@ public class AirspyHfTunerEditor extends TunerEditor<AirspyHfTuner,AirspyHfTuner
|
||||
if(mAttenuationCombo == null)
|
||||
{
|
||||
mAttenuationCombo = new JComboBox<>(Attenuation.values());
|
||||
mAttenuationCombo.setToolTipText("Attenuation applies when the AGC is off");
|
||||
mAttenuationCombo.setEnabled(false);
|
||||
mAttenuationCombo.addActionListener(e -> {
|
||||
if(!isLoading())
|
||||
@@ -270,12 +268,12 @@ public class AirspyHfTunerEditor extends TunerEditor<AirspyHfTuner,AirspyHfTuner
|
||||
getTuner().getController().setAttenuation(selected);
|
||||
save();
|
||||
}
|
||||
catch(IOException ioe)
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error setting Airspy Hf attenuation [" + selected + "]", ioe);
|
||||
mLog.error("Error setting Airspy Hf attenuation [" + selected + "]", se);
|
||||
JOptionPane.showMessageDialog(AirspyHfTunerEditor.this,
|
||||
"Airspy Tuner Controller - couldn't apply attenuation setting [" +
|
||||
selected + "] " + ioe.getLocalizedMessage());
|
||||
selected + "] " + se.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -300,13 +298,14 @@ public class AirspyHfTunerEditor extends TunerEditor<AirspyHfTuner,AirspyHfTuner
|
||||
try
|
||||
{
|
||||
getTuner().getController().setAgc(mAgcToggleButton.isSelected());
|
||||
getAttenuationCombo().setEnabled(!mAgcToggleButton.isSelected());
|
||||
save();
|
||||
}
|
||||
catch(IOException ioe)
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error setting Airspy HF AGC", ioe);
|
||||
mLog.error("Error setting Airspy HF AGC", se);
|
||||
JOptionPane.showMessageDialog(AirspyHfTunerEditor.this,
|
||||
"Airspy HF Tuner Controller - couldn't change AGC setting" + ioe.getLocalizedMessage());
|
||||
"Airspy HF Tuner Controller - couldn't change AGC setting" + se.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -333,11 +332,11 @@ public class AirspyHfTunerEditor extends TunerEditor<AirspyHfTuner,AirspyHfTuner
|
||||
getTuner().getController().setLna(mLnaToggleButton.isSelected());
|
||||
save();
|
||||
}
|
||||
catch(IOException ioe)
|
||||
catch(SourceException se)
|
||||
{
|
||||
mLog.error("Error setting Airspy HF LNA", ioe);
|
||||
mLog.error("Error setting Airspy HF LNA", se);
|
||||
JOptionPane.showMessageDialog(AirspyHfTunerEditor.this,
|
||||
"Airspy HF Tuner Controller - couldn't change LNA setting" + ioe.getLocalizedMessage());
|
||||
"Airspy HF Tuner Controller - couldn't change LNA setting" + se.getLocalizedMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2023 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
* ****************************************************************************
|
||||
*/
|
||||
|
||||
package io.github.dsheirer.source.tuner.airspy.hf;
|
||||
|
||||
/**
|
||||
* Requests/Commands supported by the Airspy HF
|
||||
*/
|
||||
public enum Request
|
||||
{
|
||||
INVALID(0),
|
||||
RECEIVER_MODE(1),
|
||||
SET_FREQUENCY(2),
|
||||
GET_SAMPLE_RATES(3),
|
||||
SET_SAMPLE_RATE(4),
|
||||
CONFIG_READ(5),
|
||||
CONFIG_WRITE(6),
|
||||
GET_SERIAL_NUMBER_BOARD_ID(7),
|
||||
SET_USER_OUTPUT(8),
|
||||
GET_VERSION_STRING(9),
|
||||
SET_HF_AGC(10),
|
||||
SET_HF_AGC_THRESHOLD(11),
|
||||
SET_HF_ATT(12),
|
||||
SET_HF_LNA(13),
|
||||
GET_SAMPLE_RATE_ARCHITECTURES(14),
|
||||
GET_FILTER_GAIN(15),
|
||||
GET_FREQUENCY_DELTA(16),
|
||||
SET_VCTCXO_CALIBRATION(17);
|
||||
|
||||
private int mValue;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
* @param value of the entry
|
||||
*/
|
||||
Request(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Value associated with the entry
|
||||
* @return value cast to a byte.
|
||||
*/
|
||||
public byte getRequest()
|
||||
{
|
||||
return (byte)mValue;
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package io.github.dsheirer.source.tuner.airspy.hf;
|
||||
|
||||
/**
|
||||
* Responses to Airspy HF requests
|
||||
*/
|
||||
public enum Response
|
||||
{
|
||||
SUCCESS(0),
|
||||
ERROR(-1),
|
||||
UNSUPPORTED(-2);
|
||||
|
||||
private int mValue;
|
||||
|
||||
Response(int value)
|
||||
{
|
||||
mValue = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the enumeration entry from the value.
|
||||
* @param value to lookup
|
||||
* @return entry or UNSUPPORTED if there are no matches.
|
||||
*/
|
||||
public static Response fromValue(int value)
|
||||
{
|
||||
return switch(value)
|
||||
{
|
||||
case 0 -> SUCCESS;
|
||||
case -1 -> ERROR;
|
||||
default -> UNSUPPORTED;
|
||||
};
|
||||
}
|
||||
}
|
||||
1
src/main/resources/52-airspyhf.rules
Executable file
1
src/main/resources/52-airspyhf.rules
Executable file
@@ -0,0 +1 @@
|
||||
ATTR{idVendor}=="03eb", ATTR{idProduct}=="800c", SYMLINK+="airspyhf-%k", MODE="660", GROUP="plugdev"
|
||||
Reference in New Issue
Block a user