From ec7efa4d1790f084a4e610fd85d05c6feb2ed40b Mon Sep 17 00:00:00 2001 From: Denny Sheirer Date: Sun, 8 Nov 2026 04:05:58 -0500 Subject: [PATCH] #536 Add Bias-T support for Airspy, Airspy HF and Funcube Dongles. (#2599) Co-authored-by: denny --- .../airspy/AirspyTunerConfiguration.java | 12 +++ .../tuner/airspy/AirspyTunerController.java | 23 ++++- .../tuner/airspy/AirspyTunerEditor.java | 38 ++++++++ .../airspy/hf/AirspyHfTunerConfiguration.java | 20 ++++ .../airspy/hf/AirspyHfTunerController.java | 97 +++++++++++++++++++ .../tuner/airspy/hf/AirspyHfTunerEditor.java | 48 +++++++++ .../fcd/proV1/FCD1TunerConfiguration.java | 12 +++ .../tuner/fcd/proV1/FCD1TunerController.java | 53 ++++++++++ .../tuner/fcd/proV1/FCD1TunerEditor.java | 39 ++++++++ .../fcd/proplusV2/FCD2TunerConfiguration.java | 12 +++ .../fcd/proplusV2/FCD2TunerController.java | 44 +++++++++ .../tuner/fcd/proplusV2/FCD2TunerEditor.java | 44 ++++++++- 12 files changed, 439 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java index 2aac26c4..e856b492 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerConfiguration.java @@ -32,6 +32,7 @@ public class AirspyTunerConfiguration extends TunerConfiguration private int mLNAGain = AirspyTunerController.LNA_GAIN_DEFAULT; private boolean mMixerAGC = false; private boolean mLNAAGC = false; + private boolean mBiasT = false; /** * Default constructor for JAXB @@ -129,4 +130,15 @@ public class AirspyTunerConfiguration extends TunerConfiguration { mLNAAGC = enabled; } + + @JacksonXmlProperty(isAttribute = true, localName = "bias_t") + public boolean isBiasT() + { + return mBiasT; + } + + public void setBiasT(boolean enabled) + { + mBiasT = enabled; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java index b43edfe3..841b041e 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java @@ -183,13 +183,22 @@ public class AirspyTunerController extends TunerController } /** - * Closes the device, which also stops streaming. + * Turns off the bias-T and closes the device, which also stops streaming. * @param device to close, or null */ private static void closeDevice(AirspyDevice device) { if(device != null) { + try + { + device.setBiasTee(false); + } + catch(AirspyException | RuntimeException e) + { + mLog.warn("Error turning off Airspy bias-T before closing the device - " + e.getMessage()); + } + try { device.close(); @@ -462,6 +471,7 @@ public class AirspyTunerController extends TunerController setLNAGain(config.getLNAGain()); setMixerAGC(config.isMixerAGC()); setLNAAGC(config.isLNAAGC()); + setBiasT(config.isBiasT()); //Set the gain mode last, so custom values are already set, and linearity and sensitivity modes will //automatically override the custom values. @@ -620,6 +630,17 @@ public class AirspyTunerController extends TunerController execute(device -> device.setLnaAgc(enabled)); } + /** + * Enables/disables the Bias-T (RF bias tee) power output for active antennas + * + * @param enabled true to enable Bias-T, false to disable + * @throws SourceException on error + */ + public void setBiasT(boolean enabled) throws SourceException + { + execute(device -> device.setBiasTee(enabled)); + } + public void setGain(Gain gain) throws SourceException { if(gain != Gain.CUSTOM) diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java index fc684e3b..542d73a1 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java @@ -66,6 +66,7 @@ public class AirspyTunerEditor extends TunerEditor + { + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getController().setBiasT(mBiasTCheckBox.isSelected()); + save(); + } + catch(Exception e1) + { + mLog.error("Error setting Bias-T", e1); + JOptionPane.showMessageDialog(mBiasTCheckBox, "Couldn't set Bias-T: " + e1.getMessage()); + } + } + }); + } + + return mBiasTCheckBox; + } + private JCheckBox getLNAAGCCheckBox() { if(mLNAAGCCheckBox == null) @@ -607,6 +644,7 @@ public class AirspyTunerEditor extends TunerEditor 0) + { + device.setBiasTee(0); + return true; + } + + return false; + } + catch(AirspyHfException | RuntimeException e) + { + //Bias-T count not supported - fall through and probe with a bias-T off command. + } + + try + { + device.setBiasTee(0); + return true; + } + catch(AirspyHfException | RuntimeException e) + { + mLog.debug("Airspy HF firmware doesn't support bias-T - " + e.getMessage()); + return false; + } + } + /** * Closes the device, which also stops streaming. * @param device to close, or null @@ -448,6 +506,11 @@ public class AirspyHfTunerController extends TunerController setLna(airspyConfig.isLna()); setAttenuation(airspyConfig.getAttenuation()); setAgc(airspyConfig.isAgc()); + + if(isBiasTSupported()) + { + setBiasT(airspyConfig.isBiasT()); + } } catch(SourceException se) { @@ -670,4 +733,38 @@ public class AirspyHfTunerController extends TunerController execute(device -> device.setAttenuationStep(attenuation.getValue())); mAttenuation = attenuation; } + + /** + * Indicates if the device firmware supports a bias-T, available once the tuner is started. + * @return true if supported + */ + public boolean isBiasTSupported() + { + return mBiasTSupported; + } + + /** + * Indicates if the Bias-T power output is enabled. + * @return enabled state + */ + public boolean getBiasT() + { + return mBiasTEnabled; + } + + /** + * Enables/disables the Bias-T (RF bias tee) power output for active antennas + * @param enabled true to enable Bias-T, false to disable + * @throws SourceException if the device doesn't support a bias-T or there is an error + */ + public void setBiasT(boolean enabled) throws SourceException + { + if(!mBiasTSupported) + { + throw new SourceException("Airspy HF firmware doesn't support bias-T"); + } + + execute(device -> device.setBiasTee(enabled ? 1 : 0)); + mBiasTEnabled = enabled; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/hf/AirspyHfTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/hf/AirspyHfTunerEditor.java index 2fa78e35..e5266b1d 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/hf/AirspyHfTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/hf/AirspyHfTunerEditor.java @@ -46,6 +46,7 @@ public class AirspyHfTunerEditor extends TunerEditor mAttenuationCombo; private JToggleButton mAgcToggleButton; private JToggleButton mLnaToggleButton; + private JToggleButton mBiasTToggleButton; /** * Constructs an instance @@ -100,6 +101,7 @@ public class AirspyHfTunerEditor extends TunerEditor { + if(hasTuner() && !isLoading()) + { + try + { + getTuner().getController().setBiasT(mBiasTToggleButton.isSelected()); + save(); + } + catch(SourceException se) + { + mLog.error("Error setting Airspy HF Bias-T", se); + mBiasTToggleButton.setSelected(getTuner().getController().getBiasT()); + JOptionPane.showMessageDialog(AirspyHfTunerEditor.this, + "Airspy HF Tuner Controller - couldn't change Bias-T setting " + se.getLocalizedMessage()); + } + } + }); + } + + return mBiasTToggleButton; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java index 73927d04..292457ad 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerConfiguration.java @@ -35,6 +35,7 @@ public class FCD1TunerConfiguration extends TunerConfiguration private LNAGain mLNAGain = LNAGain.LNA_GAIN_PLUS_20_0; private LNAEnhance mLNAEnhance = LNAEnhance.LNA_ENHANCE_OFF; private MixerGain mMixerGain = MixerGain.MIXER_GAIN_PLUS_12_0; + private boolean mBiasT = false; /** * Default constructor for JAXB @@ -132,4 +133,15 @@ public class FCD1TunerConfiguration extends TunerConfiguration { mGainCorrection = value; } + + @JacksonXmlProperty(isAttribute = true, localName = "bias_t") + public boolean isBiasT() + { + return mBiasT; + } + + public void setBiasT(boolean enabled) + { + mBiasT = enabled; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java index bc1fa859..f8da5af7 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerController.java @@ -49,6 +49,7 @@ public class FCD1TunerController extends FCDTunerController private LNAGain mLNAGain; private LNAEnhance mLNAEnhance; private MixerGain mMixerGain; + private boolean mBiasTEnabled = false; /** * Constructs an instance @@ -123,9 +124,61 @@ public class FCD1TunerController extends FCDTunerController //Re-cast any usb exceptions as source exceptions throw new SourceException("FCD 1 Controller error applying tuner config - " + e.getLocalizedMessage()); } + + //Apply the bias-T separately so that a bias-T error doesn't prevent the tuner from starting. + try + { + setBiasT(fcd1.isBiasT()); + } + catch(SourceException se) + { + mLog.warn("FCD 1 Controller couldn't apply bias-T setting - " + se.getMessage()); + } } } + /** + * Enables/disables the Bias-T (RF bias tee) power output for active antennas + * + * @param enabled true to enable Bias-T, false to disable + * @throws SourceException on error + */ + public void setBiasT(boolean enabled) throws SourceException + { + try + { + send(FCDCommand.APP_SET_BIAS_TEE, enabled ? 1 : 0); + mBiasTEnabled = enabled; + } + catch(Exception e) + { + throw new SourceException("Couldn't set FCD Pro bias-T", e); + } + } + + /** + * Turns off the bias-T, when enabled, before closing the device. + */ + @Override + public void stop() + { + if(mBiasTEnabled) + { + try + { + setBiasT(false); + } + catch(Exception e) + { + mLog.warn("Error turning off FCD Pro bias-T before closing the device - " + e.getMessage()); + } + + mBiasTEnabled = false; + } + + super.stop(); + } + /** * Gets the current LNA Gain value from the controller and stores it */ diff --git a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java index 8b480b5a..bd34a42a 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -19,6 +19,7 @@ package io.github.dsheirer.source.tuner.fcd.proV1; import io.github.dsheirer.preference.UserPreferences; +import io.github.dsheirer.source.SourceException; import io.github.dsheirer.source.tuner.fcd.FCDTuner; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; import io.github.dsheirer.source.tuner.fcd.proV1.FCD1TunerController.LNAGain; @@ -32,6 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; @@ -52,6 +54,7 @@ public class FCD1TunerEditor extends TunerEditor mLnaGainCombo; private JComboBox mLnaEnhanceCombo; private JComboBox mMixerGainCombo; + private JCheckBox mBiasTCheckBox; private CorrectionSpinner mCorrectionDCI; private CorrectionSpinner mCorrectionDCQ; private CorrectionSpinner mGainCorrectionSpinner; @@ -121,6 +124,7 @@ public class FCD1TunerEditor extends TunerEditor + { + if(hasTuner() && !isLoading()) + { + try + { + getController().setBiasT(mBiasTCheckBox.isSelected()); + save(); + } + catch(SourceException e) + { + mLog.error("Couldn't set bias-T for FCD Pro", e); + JOptionPane.showMessageDialog(FCD1TunerEditor.this, "Couldn't set Bias-T: " + e.getMessage()); + } + } + }); + } + + return mBiasTCheckBox; + } + public JComboBox getLnaEnhanceCombo() { if(mLnaEnhanceCombo == null) @@ -386,6 +424,7 @@ public class FCD1TunerEditor extends TunerEditor + { + if(hasTuner() && !isLoading()) + { + try + { + getController().setBiasT(getBiasTCheckBox().isSelected()); + save(); + } + catch(SourceException e) + { + mLog.error("Couldn't set bias-T for FCD2", e); + JOptionPane.showMessageDialog(mBiasTCheckBox, "Couldn't set Bias-T: " + e.getMessage()); + } + } + }); + } + + return mBiasTCheckBox; + } + @Override public void setTunerLockState(boolean locked) { @@ -255,6 +294,7 @@ public class FCD2TunerEditor extends TunerEditor