Skip to content

#1473 RSP Tuners - Provide IF AGC Control & Power Overload Indicators #1484

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.tuner.GainReduction;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.tuner.IfMode;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.tuner.LoMode;
import io.github.dsheirer.util.ThreadPool;
import java.lang.ref.WeakReference;
import java.util.concurrent.locks.ReentrantLock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -46,6 +48,7 @@ public abstract class ControlRsp<T extends Device> implements IControlRsp
protected int mGain;
private IDeviceEventListener mDeviceEventListener;
private IStreamListener mStreamListener;
protected WeakReference<IGainOverloadListener> mGainOverloadReference;

//Streaming control lock and boolean status indicator. Access to the boolean indicator is protected by the lock.
protected ReentrantLock mStreamingLock = new ReentrantLock();
Expand Down Expand Up @@ -77,8 +80,7 @@ public void start() throws SDRPlayException
getDevice().getCompositeParameters().getControlAParameters().getDcOffset().setDC(true);
getDevice().getCompositeParameters().getControlAParameters().getDcOffset().setIQ(true);

//Setup IF, LO, and AGC Mode
getDevice().getCompositeParameters().getControlAParameters().getAgc().setAgcMode(AgcMode.DISABLE);
//Setup IF & LO Mode
getDevice().setIfMode(IfMode.IF_ZERO);
getDevice().setLoMode(LoMode.AUTO);
}
Expand Down Expand Up @@ -257,6 +259,20 @@ public void setGain(int gain) throws SDRPlayException
}
}

/**
* Registers the listener to receive gain overload notifications.
* @param listener to register
*/
@Override
public void setGainOverloadListener(IGainOverloadListener listener)
{
if(mGainOverloadReference != null && mGainOverloadReference.get() != null)
{
mGainOverloadReference.clear();
}

mGainOverloadReference = new WeakReference<>(listener);
}

@Override
public void acknowledgePowerOverload(TunerSelect tunerSelect) throws SDRPlayException
Expand All @@ -269,6 +285,17 @@ public void acknowledgePowerOverload(TunerSelect tunerSelect) throws SDRPlayExce
{
throw new SDRPlayException("Device is not initialized");
}

//Notify an optional weakly referenced, registered listener that gain overload has been acknowledged.
if(mGainOverloadReference != null)
{
IGainOverloadListener listener = mGainOverloadReference.get();

if(listener != null)
{
ThreadPool.CACHED.submit(() -> listener.notifyGainOverload(tunerSelect));
}
}
}

@Override
Expand Down Expand Up @@ -339,6 +366,27 @@ protected void validateGain(int gain) throws SDRPlayException
}
}

/**
* Current IF AGC mode setting.
* @return AGC mode.
*/
@Override
public AgcMode getAgcMode()
{
return getDevice().getCompositeParameters().getControlAParameters().getAgc().getAgcMode();
}

/**
* Sets the IF AGC mode
* @param mode to set.
*/
@Override
public void setAgcMode(AgcMode mode) throws SDRPlayException
{
getDevice().getCompositeParameters().getControlAParameters().getAgc().setAgcMode(mode);
getDevice().update(getTunerSelect(), UpdateReason.CONTROL_AGC);
}

/**
* Registers listeners to receive device events and sample streams.
* @param deviceEventListener to receive device events
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import io.github.dsheirer.source.tuner.sdrplay.api.callback.IStreamListener;
import io.github.dsheirer.source.tuner.sdrplay.api.device.Device;
import io.github.dsheirer.source.tuner.sdrplay.api.device.TunerSelect;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.control.AgcMode;

/**
* Control interface for base RSP device
Expand Down Expand Up @@ -125,4 +126,22 @@ public interface IControlRsp
* @return gain index value (0 - 28)
*/
int getGain();

/**
* Current IF AGC mode setting.
* @return IF agc mode
*/
AgcMode getAgcMode();

/**
* Sets the IF AGC mode
* @param mode to set.
*/
void setAgcMode(AgcMode mode) throws SDRPlayException;

/**
* Registers a listener to receive notifications of gain overload.
* @param listener to register
*/
void setGainOverloadListener(IGainOverloadListener listener);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* *****************************************************************************
* 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.sdrplay;

import io.github.dsheirer.source.tuner.sdrplay.api.device.TunerSelect;

/**
* Listener interface to receive notifications of gain overload in the RSP device.
*/
public interface IGainOverloadListener
{
void notifyGainOverload(TunerSelect tunerSelect);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@

/**
* RSP device Sample Rate, Bandwidth and Decimation enumeration
*
* Note: final effective sample rate must be greater than IF bandwidth setting to avoid aliasing. The available IF
* bandwidth values effectively dictate the available sample rates
*/
public enum RspSampleRate
{
RATE_0_250(8_000_000, 016_000, Bandwidth.BW_0_300, Decimate.X32, "0.250 MHz (0.234 usable)"),
RATE_0_500(8_000_000, 024_000, Bandwidth.BW_0_600, Decimate.X16, "0.500 MHz (0.476 usable)"),
RATE_0_250(8_000_000, 16_000, Bandwidth.BW_0_300, Decimate.X32, "0.250 MHz (0.234 usable)"),
RATE_0_500(8_000_000, 24_000, Bandwidth.BW_0_600, Decimate.X16, "0.500 MHz (0.476 usable)"),
RATE_1_000(8_000_000, 100_000, Bandwidth.BW_1_536, Decimate.X8, "1.000 MHz (0.900 usable)"),
RATE_1_500(6_000_000, 140_000, Bandwidth.BW_1_536, Decimate.X4, "1.500 MHz (1.360 usable)"),
RATE_2_048(8_192_000, 248_000, Bandwidth.BW_1_536, Decimate.X4, "2.048 MHz (1.800 usable)"),
RATE_3_000(6_000_000, 300_000, Bandwidth.BW_5_000, Decimate.X2, "3.000 MHz (2.700 usable)"),
RATE_4_000(8_000_000, 340_000, Bandwidth.BW_5_000, Decimate.X2, "4.000 MHz (3.560 usable)"),
RATE_5_000(5_000_000, 880_000, Bandwidth.BW_5_000, Decimate.X1, "5.000 MHz (4.120 usable)"),
Expand All @@ -55,11 +55,11 @@ public enum RspSampleRate

UNDEFINED(0, 0, Bandwidth.UNDEFINED, Decimate.X1, "UNDEFINED");

private long mSampleRate;
private long mUnusable;
private Bandwidth mBandwidth;
private Decimate mDecimation;
private String mDescription;
private final long mSampleRate;
private final long mUnusable;
private final Bandwidth mBandwidth;
private final Decimate mDecimation;
private final String mDescription;

/**
* Constructs an instance
Expand All @@ -81,12 +81,12 @@ public enum RspSampleRate
/**
* Single tuner sample rates for all devices operating in single tuner mode
*/
public static EnumSet<RspSampleRate> SINGLE_TUNER_SAMPLE_RATES = EnumSet.range(RATE_0_250, RATE_10_000);
public static final EnumSet<RspSampleRate> SINGLE_TUNER_SAMPLE_RATES = EnumSet.range(RATE_0_250, RATE_10_000);

/**
* RSPduo dual-tuner mode sample rates
*/
public static EnumSet<RspSampleRate> DUAL_TUNER_SAMPLE_RATES = EnumSet.range(DUO_RATE_0_500, DUO_RATE_2_000);
public static final EnumSet<RspSampleRate> DUAL_TUNER_SAMPLE_RATES = EnumSet.range(DUO_RATE_0_500, DUO_RATE_2_000);

/**
* Sample Rate
Expand Down Expand Up @@ -137,14 +137,6 @@ public Decimate getDecimation()
return mDecimation;
}

/**
* Indicates if this sample rate specifies decimation
*/
public boolean hasDecimation()
{
return getDecimation().isEnabled();
}

/**
* Effective Sample Rate (sample rate / decimation). Note: for RSPduo operating in dual-tuner mode, an
* inherent x4 decimation is applied in the hardware.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import io.github.dsheirer.source.tuner.configuration.TunerConfiguration;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.control.AgcMode;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.tuner.GainReduction;
import io.github.dsheirer.source.tuner.sdrplay.rsp1a.Rsp1aTunerConfiguration;
import io.github.dsheirer.source.tuner.sdrplay.rsp2.Rsp2TunerConfiguration;
Expand All @@ -33,7 +34,7 @@
/**
* Abstract RSP tuner configuration
*/
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = Rsp1aTunerConfiguration.class, name = "rsp1aTunerConfiguration"),
@JsonSubTypes.Type(value = Rsp2TunerConfiguration.class, name = "rsp2TunerConfiguration"),
Expand All @@ -47,7 +48,8 @@ public abstract class RspTunerConfiguration extends TunerConfiguration
public static final RspSampleRate DEFAULT_DUAL_TUNER_SAMPLE_RATE = RspSampleRate.DUO_RATE_2_000;

private RspSampleRate mRspSampleRate = DEFAULT_SINGLE_TUNER_SAMPLE_RATE;
private int mGain = 14;
private int mGain = 24;
private AgcMode mAgcMode = AgcMode.ENABLE;

/**
* JAXB Constructor
Expand Down Expand Up @@ -103,4 +105,23 @@ public void setGain(int gain)
mGain = gain;
}
}

/**
* IF AGC mode
* @return mode
*/
@JacksonXmlProperty(isAttribute = true, localName = "agcMode")
public AgcMode getAgcMode()
{
return mAgcMode;
}

/**
* Sets the IF AGC mode
* @param mode to set
*/
public void setAgcMode(AgcMode mode)
{
mAgcMode = mode;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,36 @@

import io.github.dsheirer.preference.UserPreferences;
import io.github.dsheirer.source.tuner.manager.TunerManager;
import io.github.dsheirer.source.tuner.sdrplay.api.SDRPlayException;
import io.github.dsheirer.source.tuner.sdrplay.api.device.TunerSelect;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.control.AgcMode;
import io.github.dsheirer.source.tuner.sdrplay.api.parameter.tuner.GainReduction;
import io.github.dsheirer.source.tuner.ui.TunerEditor;
import io.github.dsheirer.util.ThreadPool;
import java.awt.Color;
import java.awt.EventQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSlider;

/**
* Abstract RSP tuner editor
*/
public abstract class RspTunerEditor<C extends RspTunerConfiguration> extends TunerEditor<RspTuner,C>
public abstract class RspTunerEditor<C extends RspTunerConfiguration> extends TunerEditor<RspTuner,C> implements IGainOverloadListener
{
private Logger mLog = LoggerFactory.getLogger(RspTunerEditor.class);
private JSlider mGainSlider;
private JLabel mGainValueLabel;
private JComboBox<AgcMode> mAgcModeCombo;
private JButton mGainOverloadButton;
private AtomicBoolean mGainOverloadAlert = new AtomicBoolean();

/**
* Constructs an instance
Expand Down Expand Up @@ -105,4 +118,87 @@ protected JLabel getGainValueLabel()

return mGainValueLabel;
}

/**
* IF AGC mode combobox control
*/
protected JComboBox<AgcMode> getAgcModeCombo()
{
if(mAgcModeCombo == null)
{
mAgcModeCombo = new JComboBox<>(AgcMode.values());
mAgcModeCombo.setEnabled(false);
mAgcModeCombo.addActionListener(e -> {
if(hasTuner() && !isLoading())
{
AgcMode selected = (AgcMode)mAgcModeCombo.getSelectedItem();
try
{
getTunerController().getControlRsp().setAgcMode(selected);
save();
}
catch(SDRPlayException se)
{
mLog.error("Error setting AGC mode on RSP device");
}
save();
}
});
}

return mAgcModeCombo;
}

/**
* Gain overload button. Used in a disabled state to indicate (e.g. flashing color) that gain overload is detected.
*/
protected JButton getGainOverloadButton()
{
if(mGainOverloadButton == null)
{
mGainOverloadButton = new JButton("Gain Overload");
mGainOverloadButton.setToolTipText("Notification that manual gain is set too high and causing power overload. Reduce manual gain when this flashes.");
mGainOverloadButton.setEnabled(false);
}

return mGainOverloadButton;
}

@Override
public void notifyGainOverload(TunerSelect tunerSelect)
{
if(hasTuner() && getTuner().getRspTunerController().getTunerSelect() == tunerSelect)
{
//Set overload alert
EventQueue.invokeLater(() -> setGainOverloadAlert(true));

//Schedule a reset to happen 1 second later
ThreadPool.SCHEDULED.schedule((Runnable) () -> setGainOverloadAlert(false), 600, TimeUnit.MILLISECONDS);
}
}

/**
* Toggles the alert styling of the disabled gain overload button to indicate an alert condition or a normal
* operating condition.
* @param alert true to apply alert styling or false to reset.
*/
private void setGainOverloadAlert(boolean alert)
{
if(alert && mGainOverloadAlert.compareAndSet(false, true))
{
getGainOverloadButton().setEnabled(true);
getGainOverloadButton().setForeground(Color.YELLOW);
getGainOverloadButton().setBackground(Color.RED);
}
else if(!alert && mGainOverloadAlert.compareAndSet(true, false))
{
getGainOverloadButton().setEnabled(false);
getGainOverloadButton().setForeground(getForeground());
getGainOverloadButton().setBackground(getBackground());
}
else
{
mLog.info("Ignoring duplicate gain alerting - alert[" + alert + "] atomic [" + mGainOverloadAlert.get() + "]");
}
}
}
Loading