mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
#35 Updates AM & NBFM decoders to use Adaptive Squelch, a power squelch that includes an adaptive noise floor monitor. Implements vectorized magnitude operations for AM & FM decoders. Adds talkgroup assignment and squelch controls to AM channel configuration editor. Implements new AM gain control that attempts to normalize audio gain quickly at beginning of call/transmission and maintains that gain across lifecycle of the call and resets the gain for the next call. (#1540)
Notes on usage: - When using either AM or FM squelch control use either the Heterodyne channelizer or if using the Polyphase channelizer, avoid using trunk tracking decoders like DMR or P25 or set the squelch threshold high enough to avoid changes in noise floor from polyphase channelizer where it can vary between using the one channel or the two-channel combined channel sources. Co-authored-by: Dennis Sheirer <dsheirer@github.com>
This commit is contained in:
@@ -18,7 +18,7 @@
|
||||
*/
|
||||
package io.github.dsheirer.gui.instrument.chart;
|
||||
|
||||
import io.github.dsheirer.buffer.RealCircularBuffer;
|
||||
import io.github.dsheirer.buffer.FloatCircularBuffer;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
@@ -34,7 +34,7 @@ public class RealSampleLineChart extends LineChart implements Listener<float[]>
|
||||
private final static Logger mLog = LoggerFactory.getLogger(RealSampleLineChart.class);
|
||||
private ObservableList<Data<Integer,Float>> mSamples = FXCollections.observableArrayList();
|
||||
private Series<Integer,Float> mSampleSeries = new Series<>("Samples", mSamples);
|
||||
private RealCircularBuffer mRealCircularBuffer;
|
||||
private FloatCircularBuffer mRealCircularBuffer;
|
||||
|
||||
public RealSampleLineChart(int length, double tickUnit)
|
||||
{
|
||||
@@ -50,7 +50,7 @@ public class RealSampleLineChart extends LineChart implements Listener<float[]>
|
||||
|
||||
private void init(int length)
|
||||
{
|
||||
mRealCircularBuffer = new RealCircularBuffer(length);
|
||||
mRealCircularBuffer = new FloatCircularBuffer(length);
|
||||
|
||||
for(int x = 0; x < length; x++)
|
||||
{
|
||||
@@ -74,7 +74,7 @@ public class RealSampleLineChart extends LineChart implements Listener<float[]>
|
||||
mRealCircularBuffer.put(sample);
|
||||
}
|
||||
|
||||
float[] bufferSamples = mRealCircularBuffer.get();
|
||||
float[] bufferSamples = mRealCircularBuffer.getAll();
|
||||
|
||||
for(int x = 0; x < bufferSamples.length; x++)
|
||||
{
|
||||
|
||||
@@ -677,6 +677,10 @@ public class AliasItemEditor extends Editor<Alias>
|
||||
mAddIdentifierButton.setMaxWidth(Double.MAX_VALUE);
|
||||
mAddIdentifierButton.setDisable(true);
|
||||
|
||||
Menu amMenu = new ProtocolMenu(Protocol.AM);
|
||||
amMenu.getItems().add(new AddTalkgroupItem(Protocol.AM));
|
||||
amMenu.getItems().add(new AddTalkgroupRangeItem(Protocol.AM));
|
||||
|
||||
Menu p25Menu = new ProtocolMenu(Protocol.APCO25);
|
||||
p25Menu.getItems().add(new AddTalkgroupItem(Protocol.APCO25));
|
||||
p25Menu.getItems().add(new AddTalkgroupRangeItem(Protocol.APCO25));
|
||||
@@ -712,6 +716,7 @@ public class AliasItemEditor extends Editor<Alias>
|
||||
|
||||
Menu nbfmMenu = new ProtocolMenu(Protocol.NBFM);
|
||||
nbfmMenu.getItems().add(new AddTalkgroupItem(Protocol.NBFM));
|
||||
nbfmMenu.getItems().add(new AddTalkgroupRangeItem(Protocol.NBFM));
|
||||
|
||||
Menu passportMenu = new ProtocolMenu(Protocol.PASSPORT);
|
||||
passportMenu.getItems().add(new AddTalkgroupItem(Protocol.PASSPORT));
|
||||
@@ -725,7 +730,7 @@ public class AliasItemEditor extends Editor<Alias>
|
||||
Menu lojackMenu = new ProtocolMenu(Protocol.LOJACK);
|
||||
lojackMenu.getItems().add(new AddLojackItem());
|
||||
|
||||
mAddIdentifierButton.getItems().addAll(p25Menu, dmrMenu, fleetsyncMenu, ltrMenu, mdcMenu, mptMenu,
|
||||
mAddIdentifierButton.getItems().addAll(amMenu, p25Menu, dmrMenu, fleetsyncMenu, ltrMenu, mdcMenu, mptMenu,
|
||||
nbfmMenu, passportMenu, taitMenu, new SeparatorMenuItem(), lojackMenu);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 Dennis Sheirer
|
||||
* 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
|
||||
@@ -27,6 +27,8 @@ import io.github.dsheirer.gui.control.PrefixIdentFormatter;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.geometry.HPos;
|
||||
@@ -38,9 +40,6 @@ import javafx.scene.layout.GridPane;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Editor for talkgroup alias identifiers
|
||||
*/
|
||||
@@ -238,6 +237,10 @@ public class TalkgroupEditor extends IdentifierEditor<Talkgroup>
|
||||
private void loadTalkgroupDetails()
|
||||
{
|
||||
mTalkgroupDetails.clear();
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.AM, IntegerFormat.DECIMAL, new IntegerFormatter(1,0xFFFF),
|
||||
"Format: 1 - 65535"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.AM, IntegerFormat.HEXADECIMAL, new HexFormatter(1,0xFFFF),
|
||||
"Format: 1 - FFFF"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.APCO25, IntegerFormat.DECIMAL, new IntegerFormatter(0,65535),
|
||||
"Format: 0 - 65535"));
|
||||
mTalkgroupDetails.add(new TalkgroupDetail(Protocol.APCO25, IntegerFormat.HEXADECIMAL, new HexFormatter(0,65535),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -19,25 +19,44 @@
|
||||
|
||||
package io.github.dsheirer.gui.playlist.channel;
|
||||
|
||||
import io.github.dsheirer.gui.control.DbPowerMeter;
|
||||
import io.github.dsheirer.gui.control.HexFormatter;
|
||||
import io.github.dsheirer.gui.control.IntegerFormatter;
|
||||
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
|
||||
import io.github.dsheirer.gui.playlist.source.FrequencyEditor;
|
||||
import io.github.dsheirer.gui.playlist.source.SourceConfigurationEditor;
|
||||
import io.github.dsheirer.module.decode.DecoderType;
|
||||
import io.github.dsheirer.module.decode.am.DecodeConfigAM;
|
||||
import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.module.decode.nbfm.DecodeConfigNBFM;
|
||||
import io.github.dsheirer.module.log.EventLogType;
|
||||
import io.github.dsheirer.module.log.config.EventLogConfiguration;
|
||||
import io.github.dsheirer.playlist.PlaylistManager;
|
||||
import io.github.dsheirer.preference.UserPreferences;
|
||||
import io.github.dsheirer.preference.identifier.IntegerFormat;
|
||||
import io.github.dsheirer.protocol.Protocol;
|
||||
import io.github.dsheirer.record.RecorderType;
|
||||
import io.github.dsheirer.record.config.RecordConfiguration;
|
||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.TextField;
|
||||
import javafx.scene.control.TextFormatter;
|
||||
import javafx.scene.control.TitledPane;
|
||||
import javafx.scene.control.Toggle;
|
||||
import javafx.scene.control.ToggleButton;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.text.TextAlignment;
|
||||
import org.controlsfx.control.SegmentedButton;
|
||||
import org.controlsfx.control.ToggleSwitch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -48,12 +67,23 @@ import org.slf4j.LoggerFactory;
|
||||
public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(AMConfigurationEditor.class);
|
||||
private TitledPane mSourcePane;
|
||||
private TitledPane mDecoderPane;
|
||||
private TitledPane mRecordPane;
|
||||
private TitledPane mSourcePane;
|
||||
private ToggleSwitch mAudioRecordSwitch;
|
||||
private TitledPane mEventLogPane;
|
||||
private ToggleSwitch mBasebandRecordSwitch;
|
||||
private SourceConfigurationEditor mSourceConfigurationEditor;
|
||||
private SegmentedButton mBandwidthButton;
|
||||
private TextField mTalkgroupField;
|
||||
private TextField mSquelchThresholdField;
|
||||
private ToggleSwitch mSquelchAutoTrackSwitch;
|
||||
private TextFormatter<Integer> mTalkgroupTextFormatter;
|
||||
private IntegerFormatter mSquelchTextFormatter = new IntegerFormatter((int)DbPowerMeter.DEFAULT_MINIMUM_POWER,
|
||||
(int)DbPowerMeter.DEFAULT_MAXIMUM_POWER);
|
||||
private EventLogConfigurationEditor mEventLogConfigurationEditor;
|
||||
private TalkgroupValueChangeListener mTalkgroupValueChangeListener = new TalkgroupValueChangeListener();
|
||||
private IntegerFormatter mDecimalFormatter = new IntegerFormatter(1, 65535);
|
||||
private HexFormatter mHexFormatter = new HexFormatter(1, 65535);
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
@@ -67,6 +97,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
super(playlistManager, tunerManager, userPreferences);
|
||||
getTitledPanesBox().getChildren().add(getSourcePane());
|
||||
getTitledPanesBox().getChildren().add(getDecoderPane());
|
||||
getTitledPanesBox().getChildren().add(getEventLogPane());
|
||||
getTitledPanesBox().getChildren().add(getRecordPane());
|
||||
}
|
||||
|
||||
@@ -93,13 +124,75 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
mDecoderPane = new TitledPane();
|
||||
mDecoderPane.setText("Decoder: AM");
|
||||
mDecoderPane.setExpanded(false);
|
||||
mDecoderPane.setDisable(true);
|
||||
mDecoderPane.setExpanded(true);
|
||||
|
||||
GridPane gridPane = new GridPane();
|
||||
gridPane.setPadding(new Insets(10,10,10,10));
|
||||
gridPane.setHgap(10);
|
||||
gridPane.setVgap(10);
|
||||
|
||||
int row = 0;
|
||||
|
||||
Label bandwidthLabel = new Label("Channel Bandwidth");
|
||||
GridPane.setHalignment(bandwidthLabel, HPos.RIGHT);
|
||||
GridPane.setConstraints(bandwidthLabel, 0, row);
|
||||
gridPane.getChildren().add(bandwidthLabel);
|
||||
|
||||
GridPane.setConstraints(getBandwidthButton(), 1, row++, 3, 1);
|
||||
gridPane.getChildren().add(getBandwidthButton());
|
||||
|
||||
Label squelchLabel = new Label("Squelch Threshold");
|
||||
GridPane.setHalignment(squelchLabel, HPos.RIGHT);
|
||||
GridPane.setConstraints(squelchLabel, 0, row);
|
||||
gridPane.getChildren().add(squelchLabel);
|
||||
|
||||
GridPane.setConstraints(getSquelchThresholdField(), 1, row);
|
||||
gridPane.getChildren().add(getSquelchThresholdField());
|
||||
|
||||
Label autoTrackLabel = new Label("Squelch Auto-Track");
|
||||
GridPane.setHalignment(autoTrackLabel, HPos.RIGHT);
|
||||
GridPane.setConstraints(autoTrackLabel, 2, row);
|
||||
gridPane.getChildren().add(autoTrackLabel);
|
||||
|
||||
GridPane.setConstraints(getSquelchAutoTrackSwitch(), 3, row);
|
||||
GridPane.setHalignment(getSquelchAutoTrackSwitch(), HPos.LEFT);
|
||||
gridPane.getChildren().add(getSquelchAutoTrackSwitch());
|
||||
|
||||
Label talkgroupLabel = new Label("Talkgroup To Assign");
|
||||
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
|
||||
GridPane.setConstraints(talkgroupLabel, 4, row);
|
||||
gridPane.getChildren().add(talkgroupLabel);
|
||||
|
||||
GridPane.setConstraints(getTalkgroupField(), 5, row);
|
||||
gridPane.getChildren().add(getTalkgroupField());
|
||||
|
||||
mDecoderPane.setContent(gridPane);
|
||||
|
||||
//Special handling - the pill button doesn't like to set a selected state if the pane is not expanded,
|
||||
//so detect when the pane is expanded and refresh the config view
|
||||
mDecoderPane.expandedProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if(newValue)
|
||||
{
|
||||
//Reset the config so the editor gets updated
|
||||
setDecoderConfiguration(getItem().getDecodeConfiguration());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mDecoderPane;
|
||||
}
|
||||
|
||||
private TitledPane getEventLogPane()
|
||||
{
|
||||
if(mEventLogPane == null)
|
||||
{
|
||||
mEventLogPane = new TitledPane("Logging", getEventLogConfigurationEditor());
|
||||
mEventLogPane.setExpanded(false);
|
||||
}
|
||||
|
||||
return mEventLogPane;
|
||||
}
|
||||
|
||||
private TitledPane getRecordPane()
|
||||
{
|
||||
if(mRecordPane == null)
|
||||
@@ -113,14 +206,6 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
gridPane.setHgap(10);
|
||||
gridPane.setVgap(10);
|
||||
|
||||
GridPane.setConstraints(getAudioRecordSwitch(), 0, 0);
|
||||
gridPane.getChildren().add(getAudioRecordSwitch());
|
||||
|
||||
Label recordAudioLabel = new Label("Audio");
|
||||
GridPane.setHalignment(recordAudioLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(recordAudioLabel, 1, 0);
|
||||
gridPane.getChildren().add(recordAudioLabel);
|
||||
|
||||
GridPane.setConstraints(getBasebandRecordSwitch(), 0, 1);
|
||||
gridPane.getChildren().add(getBasebandRecordSwitch());
|
||||
|
||||
@@ -149,17 +234,20 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
return mSourceConfigurationEditor;
|
||||
}
|
||||
|
||||
private ToggleSwitch getAudioRecordSwitch()
|
||||
private EventLogConfigurationEditor getEventLogConfigurationEditor()
|
||||
{
|
||||
if(mAudioRecordSwitch == null)
|
||||
if(mEventLogConfigurationEditor == null)
|
||||
{
|
||||
mAudioRecordSwitch = new ToggleSwitch();
|
||||
mAudioRecordSwitch.setDisable(true);
|
||||
mAudioRecordSwitch.setTextAlignment(TextAlignment.RIGHT);
|
||||
mAudioRecordSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
List<EventLogType> types = new ArrayList<>();
|
||||
types.add(EventLogType.CALL_EVENT);
|
||||
types.add(EventLogType.DECODED_MESSAGE);
|
||||
|
||||
mEventLogConfigurationEditor = new EventLogConfigurationEditor(types);
|
||||
mEventLogConfigurationEditor.setPadding(new Insets(5,5,5,5));
|
||||
mEventLogConfigurationEditor.modifiedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mAudioRecordSwitch;
|
||||
return mEventLogConfigurationEditor;
|
||||
}
|
||||
|
||||
private ToggleSwitch getBasebandRecordSwitch()
|
||||
@@ -176,19 +264,179 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
return mBasebandRecordSwitch;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDecoderConfiguration(DecodeConfiguration config)
|
||||
private SegmentedButton getBandwidthButton()
|
||||
{
|
||||
if(config instanceof DecodeConfigAM)
|
||||
if(mBandwidthButton == null)
|
||||
{
|
||||
DecodeConfigAM decodeConfig = (DecodeConfigAM)config;
|
||||
getAudioRecordSwitch().setDisable(false);
|
||||
getAudioRecordSwitch().selectedProperty().set(decodeConfig.getRecordAudio());
|
||||
mBandwidthButton = new SegmentedButton();
|
||||
mBandwidthButton.getStyleClass().add(SegmentedButton.STYLE_CLASS_DARK);
|
||||
mBandwidthButton.setDisable(true);
|
||||
|
||||
for(DecodeConfigNBFM.Bandwidth bandwidth : DecodeConfigNBFM.Bandwidth.AM_BANDWIDTHS)
|
||||
{
|
||||
ToggleButton toggleButton = new ToggleButton(bandwidth.toString());
|
||||
toggleButton.setUserData(bandwidth);
|
||||
mBandwidthButton.getButtons().add(toggleButton);
|
||||
}
|
||||
|
||||
mBandwidthButton.getToggleGroup().selectedToggleProperty()
|
||||
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
|
||||
//Note: there is a weird timing bug with the segmented button where the toggles are not added to
|
||||
//the toggle group until well after the control is rendered. We attempt to setItem() on the
|
||||
//decode configuration and we're unable to correctly set the bandwidth setting. As a work
|
||||
//around, we'll listen for the toggles to be added and update them here. This normally only
|
||||
//happens when we first instantiate the editor and load an item for editing the first time.
|
||||
mBandwidthButton.getToggleGroup().getToggles().addListener(new ListChangeListener<Toggle>()
|
||||
{
|
||||
@Override
|
||||
public void onChanged(Change<? extends Toggle> c)
|
||||
{
|
||||
//This change event happens when the toggles are added -- we don't need to inspect the change event
|
||||
if(getItem() != null && getItem().getDecodeConfiguration() instanceof DecodeConfigAM config)
|
||||
{
|
||||
//Capture current modified state so that we can reapply after adjusting control states
|
||||
boolean modified = modifiedProperty().get();
|
||||
|
||||
DecodeConfigAM.Bandwidth bandwidth = config.getBandwidth();
|
||||
if(bandwidth == null)
|
||||
{
|
||||
bandwidth = DecodeConfigNBFM.Bandwidth.BW_15_0;
|
||||
}
|
||||
|
||||
for(Toggle toggle: getBandwidthButton().getToggleGroup().getToggles())
|
||||
{
|
||||
toggle.setSelected(toggle.getUserData() == bandwidth);
|
||||
}
|
||||
|
||||
modifiedProperty().set(modified);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return mBandwidthButton;
|
||||
}
|
||||
|
||||
private TextField getTalkgroupField()
|
||||
{
|
||||
if(mTalkgroupField == null)
|
||||
{
|
||||
mTalkgroupField = new TextField();
|
||||
mTalkgroupField.setTextFormatter(mTalkgroupTextFormatter);
|
||||
}
|
||||
|
||||
return mTalkgroupField;
|
||||
}
|
||||
|
||||
private TextField getSquelchThresholdField()
|
||||
{
|
||||
if(mSquelchThresholdField == null)
|
||||
{
|
||||
mSquelchThresholdField = new TextField();
|
||||
mSquelchThresholdField.setTextFormatter(mSquelchTextFormatter);
|
||||
mSquelchTextFormatter.valueProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mSquelchThresholdField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Squelch noise floor auto-track feature.
|
||||
*/
|
||||
private ToggleSwitch getSquelchAutoTrackSwitch()
|
||||
{
|
||||
if(mSquelchAutoTrackSwitch == null)
|
||||
{
|
||||
mSquelchAutoTrackSwitch = new ToggleSwitch();
|
||||
mSquelchAutoTrackSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mSquelchAutoTrackSwitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the talkgroup editor's text formatter.
|
||||
* @param value to set in the control.
|
||||
*/
|
||||
private void updateTextFormatter(int value)
|
||||
{
|
||||
if(mTalkgroupTextFormatter != null)
|
||||
{
|
||||
mTalkgroupTextFormatter.valueProperty().removeListener(mTalkgroupValueChangeListener);
|
||||
}
|
||||
|
||||
IntegerFormat format = mUserPreferences.getTalkgroupFormatPreference().getTalkgroupFormat(Protocol.AM);
|
||||
|
||||
if(format == null)
|
||||
{
|
||||
format = IntegerFormat.DECIMAL;
|
||||
}
|
||||
|
||||
if(format == IntegerFormat.DECIMAL)
|
||||
{
|
||||
mTalkgroupTextFormatter = mDecimalFormatter;
|
||||
getTalkgroupField().setTooltip(new Tooltip("1 - 65,535"));
|
||||
}
|
||||
else
|
||||
{
|
||||
getAudioRecordSwitch().setDisable(true);
|
||||
getAudioRecordSwitch().selectedProperty().set(false);
|
||||
mTalkgroupTextFormatter = mDecimalFormatter;
|
||||
getTalkgroupField().setTooltip(new Tooltip("1 - FFFF"));
|
||||
}
|
||||
|
||||
mTalkgroupTextFormatter.setValue(value);
|
||||
|
||||
getTalkgroupField().setTextFormatter(mTalkgroupTextFormatter);
|
||||
mTalkgroupTextFormatter.valueProperty().addListener(mTalkgroupValueChangeListener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change listener to detect when talkgroup value has changed and set modified property to true.
|
||||
*/
|
||||
public class TalkgroupValueChangeListener implements ChangeListener<Integer>
|
||||
{
|
||||
@Override
|
||||
public void changed(ObservableValue<? extends Integer> observable, Integer oldValue, Integer newValue)
|
||||
{
|
||||
modifiedProperty().set(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDecoderConfiguration(DecodeConfiguration config)
|
||||
{
|
||||
if(config instanceof DecodeConfigAM decodeConfigAM)
|
||||
{
|
||||
getBandwidthButton().setDisable(false);
|
||||
final DecodeConfigAM.Bandwidth bandwidth = (decodeConfigAM.getBandwidth() != null ?
|
||||
decodeConfigAM.getBandwidth() : DecodeConfigNBFM.Bandwidth.BW_15_0);
|
||||
|
||||
for(Toggle toggle: getBandwidthButton().getToggleGroup().getToggles())
|
||||
{
|
||||
toggle.setSelected(toggle.getUserData() == bandwidth);
|
||||
}
|
||||
|
||||
updateTextFormatter(decodeConfigAM.getTalkgroup());
|
||||
getSquelchThresholdField().setDisable(false);
|
||||
mSquelchTextFormatter.setValue(decodeConfigAM.getSquelchThreshold());
|
||||
getSquelchAutoTrackSwitch().setDisable(false);
|
||||
getSquelchAutoTrackSwitch().setSelected(decodeConfigAM.isSquelchAutoTrack());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
getBandwidthButton().setDisable(true);
|
||||
|
||||
for(Toggle toggle: getBandwidthButton().getToggleGroup().getToggles())
|
||||
{
|
||||
toggle.setSelected(false);
|
||||
}
|
||||
|
||||
updateTextFormatter(0);
|
||||
getTalkgroupField().setDisable(true);
|
||||
getSquelchThresholdField().setDisable(true);
|
||||
getSquelchAutoTrackSwitch().setDisable(true);
|
||||
getSquelchAutoTrackSwitch().setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -197,16 +445,34 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
DecodeConfigAM config;
|
||||
|
||||
if(getItem().getDecodeConfiguration() instanceof DecodeConfigAM)
|
||||
if(getItem().getDecodeConfiguration() instanceof DecodeConfigAM decodeConfigAM)
|
||||
{
|
||||
config = (DecodeConfigAM)getItem().getDecodeConfiguration();
|
||||
config = decodeConfigAM;
|
||||
}
|
||||
else
|
||||
{
|
||||
config = new DecodeConfigAM();
|
||||
}
|
||||
|
||||
config.setRecordAudio(getAudioRecordSwitch().isSelected());
|
||||
DecodeConfigAM.Bandwidth bandwidth = DecodeConfigAM.Bandwidth.BW_15_0;
|
||||
|
||||
if(getBandwidthButton().getToggleGroup().getSelectedToggle() != null)
|
||||
{
|
||||
bandwidth = (DecodeConfigAM.Bandwidth)getBandwidthButton().getToggleGroup().getSelectedToggle().getUserData();
|
||||
}
|
||||
|
||||
config.setBandwidth(bandwidth);
|
||||
|
||||
Integer talkgroup = mTalkgroupTextFormatter.getValue();
|
||||
|
||||
if(talkgroup == null)
|
||||
{
|
||||
talkgroup = 1;
|
||||
}
|
||||
|
||||
config.setTalkgroup(talkgroup);
|
||||
config.setSquelchThreshold(mSquelchTextFormatter.getValue());
|
||||
config.setSquelchAutoTrack(getSquelchAutoTrackSwitch().isSelected());
|
||||
getItem().setDecodeConfiguration(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2022 Dennis Sheirer
|
||||
* 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
|
||||
@@ -40,6 +40,8 @@ import io.github.dsheirer.record.RecorderType;
|
||||
import io.github.dsheirer.record.config.RecordConfiguration;
|
||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||
import io.github.dsheirer.source.tuner.manager.TunerManager;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
import javafx.collections.ListChangeListener;
|
||||
@@ -59,9 +61,6 @@ import org.controlsfx.control.ToggleSwitch;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Narrow-Band FM channel configuration editor
|
||||
*/
|
||||
@@ -75,6 +74,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
private TitledPane mSourcePane;
|
||||
private TextField mTalkgroupField;
|
||||
private TextField mSquelchThresholdField;
|
||||
private ToggleSwitch mSquelchAutoTrackSwitch;
|
||||
private TextFormatter<Integer> mTalkgroupTextFormatter;
|
||||
private IntegerFormatter mSquelchTextFormatter = new IntegerFormatter((int)DbPowerMeter.DEFAULT_MINIMUM_POWER,
|
||||
(int)DbPowerMeter.DEFAULT_MAXIMUM_POWER);
|
||||
@@ -151,12 +151,21 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
GridPane.setConstraints(getSquelchThresholdField(), 3, 0);
|
||||
gridPane.getChildren().add(getSquelchThresholdField());
|
||||
|
||||
Label autoTrackLabel = new Label("Squelch Auto-Track");
|
||||
GridPane.setHalignment(autoTrackLabel, HPos.RIGHT);
|
||||
GridPane.setConstraints(autoTrackLabel, 4, 0);
|
||||
gridPane.getChildren().add(autoTrackLabel);
|
||||
|
||||
GridPane.setConstraints(getSquelchAutoTrackSwitch(), 5, 0);
|
||||
GridPane.setHalignment(getSquelchAutoTrackSwitch(), HPos.LEFT);
|
||||
gridPane.getChildren().add(getSquelchAutoTrackSwitch());
|
||||
|
||||
Label talkgroupLabel = new Label("Talkgroup To Assign");
|
||||
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
|
||||
GridPane.setConstraints(talkgroupLabel, 4, 0);
|
||||
GridPane.setConstraints(talkgroupLabel, 6, 0);
|
||||
gridPane.getChildren().add(talkgroupLabel);
|
||||
|
||||
GridPane.setConstraints(getTalkgroupField(), 5, 0);
|
||||
GridPane.setConstraints(getTalkgroupField(), 7, 0);
|
||||
gridPane.getChildren().add(getTalkgroupField());
|
||||
|
||||
mDecoderPane.setContent(gridPane);
|
||||
@@ -274,7 +283,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
mBandwidthButton.getStyleClass().add(SegmentedButton.STYLE_CLASS_DARK);
|
||||
mBandwidthButton.setDisable(true);
|
||||
|
||||
for(DecodeConfigNBFM.Bandwidth bandwidth : DecodeConfigNBFM.Bandwidth.values())
|
||||
for(DecodeConfigNBFM.Bandwidth bandwidth : DecodeConfigNBFM.Bandwidth.FM_BANDWIDTHS)
|
||||
{
|
||||
ToggleButton toggleButton = new ToggleButton(bandwidth.toString());
|
||||
toggleButton.setUserData(bandwidth);
|
||||
@@ -338,11 +347,26 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
mSquelchThresholdField = new TextField();
|
||||
mSquelchThresholdField.setTextFormatter(mSquelchTextFormatter);
|
||||
mSquelchTextFormatter.valueProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mSquelchThresholdField;
|
||||
}
|
||||
|
||||
/**
|
||||
* Squelch noise floor auto-track feature.
|
||||
*/
|
||||
private ToggleSwitch getSquelchAutoTrackSwitch()
|
||||
{
|
||||
if(mSquelchAutoTrackSwitch == null)
|
||||
{
|
||||
mSquelchAutoTrackSwitch = new ToggleSwitch();
|
||||
mSquelchAutoTrackSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mSquelchAutoTrackSwitch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the talkgroup editor's text formatter.
|
||||
* @param value to set in the control.
|
||||
@@ -421,8 +445,10 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
}
|
||||
|
||||
updateTextFormatter(decodeConfigNBFM.getTalkgroup());
|
||||
|
||||
getSquelchThresholdField().setDisable(false);
|
||||
mSquelchTextFormatter.setValue(decodeConfigNBFM.getSquelchThreshold());
|
||||
getSquelchAutoTrackSwitch().setDisable(false);
|
||||
getSquelchAutoTrackSwitch().setSelected(decodeConfigNBFM.isSquelchAutoTrack());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -435,6 +461,9 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
|
||||
updateTextFormatter(0);
|
||||
getTalkgroupField().setDisable(true);
|
||||
getSquelchThresholdField().setDisable(true);
|
||||
getSquelchAutoTrackSwitch().setDisable(true);
|
||||
getSquelchAutoTrackSwitch().setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -470,6 +499,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
|
||||
config.setTalkgroup(talkgroup);
|
||||
config.setSquelchThreshold(mSquelchTextFormatter.getValue());
|
||||
config.setSquelchAutoTrack(getSquelchAutoTrackSwitch().isSelected());
|
||||
getItem().setDecodeConfiguration(config);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.JPanel;
|
||||
@@ -94,6 +95,7 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
private JButton mSquelchUpButton;
|
||||
private JButton mSquelchDownButton;
|
||||
private JButton mLogIndexesButton;
|
||||
private JCheckBox mSquelchAutoTrackCheckBox;
|
||||
private double mSquelchThreshold;
|
||||
private boolean mPanelVisible = false;
|
||||
private boolean mDftProcessing = false;
|
||||
@@ -114,10 +116,12 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
valuePanel.setLayout(new MigLayout("", "[right][left][][]", ""));
|
||||
|
||||
mPeakLabel = new JLabel("0");
|
||||
mPeakLabel.setToolTipText("Current peak power level in decibels.");
|
||||
valuePanel.add(new JLabel("Peak:"));
|
||||
valuePanel.add(mPeakLabel, "wrap");
|
||||
|
||||
mPowerLabel = new JLabel("0");
|
||||
mPowerLabel.setToolTipText("Current Power level in decibels");
|
||||
valuePanel.add(new JLabel("Power:"));
|
||||
valuePanel.add(mPowerLabel, "wrap");
|
||||
|
||||
@@ -125,21 +129,34 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
mSquelchLabel.setEnabled(false);
|
||||
valuePanel.add(mSquelchLabel);
|
||||
mSquelchValueLabel = new JLabel(NOT_AVAILABLE);
|
||||
mSquelchValueLabel.setToolTipText("Squelch threshold value in decibels");
|
||||
mSquelchValueLabel.setEnabled(false);
|
||||
valuePanel.add(mSquelchValueLabel, "wrap");
|
||||
|
||||
IconFontSwing.register(FontAwesome.getIconFont());
|
||||
Icon iconUp = IconFontSwing.buildIcon(FontAwesome.ANGLE_UP, 12);
|
||||
mSquelchUpButton = new JButton(iconUp);
|
||||
mSquelchUpButton.setToolTipText("Increases the squelch threshold value");
|
||||
mSquelchUpButton.setEnabled(false);
|
||||
mSquelchUpButton.addActionListener(e -> broadcast(SourceEvent.requestSquelchThreshold(null, mSquelchThreshold + 1)));
|
||||
valuePanel.add(mSquelchUpButton);
|
||||
|
||||
Icon iconDown = IconFontSwing.buildIcon(FontAwesome.ANGLE_DOWN, 12);
|
||||
mSquelchDownButton = new JButton(iconDown);
|
||||
mSquelchDownButton.setToolTipText("Decreases the squelch threshold value.");
|
||||
mSquelchDownButton.setEnabled(false);
|
||||
mSquelchDownButton.addActionListener(e -> broadcast(SourceEvent.requestSquelchThreshold(null, mSquelchThreshold - 1)));
|
||||
valuePanel.add(mSquelchDownButton);
|
||||
valuePanel.add(mSquelchDownButton, "wrap");
|
||||
|
||||
mSquelchAutoTrackCheckBox = new JCheckBox("Auto Track");
|
||||
mSquelchAutoTrackCheckBox.setToolTipText("Enable or disable monitoring of the noise floor to auto-adjust the " +
|
||||
"squelch threshold value maintaining a consistent level/buffer above the noise floor");
|
||||
mSquelchAutoTrackCheckBox.setEnabled(false);
|
||||
mSquelchAutoTrackCheckBox.addActionListener(e ->
|
||||
{
|
||||
broadcast(SourceEvent.requestSquelchAutoTrack(mSquelchAutoTrackCheckBox.isSelected()));
|
||||
});
|
||||
valuePanel.add(mSquelchAutoTrackCheckBox, "span,left");
|
||||
|
||||
add(valuePanel);
|
||||
add(new JSeparator(JSeparator.VERTICAL));
|
||||
@@ -165,7 +182,7 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
});
|
||||
mNoiseFloorSpinner = new JSpinner(mNoiseFloorSpinnerModel);
|
||||
labelPanel.add(mNoiseFloorSpinner);
|
||||
labelPanel.add(new JLabel("Noise Floor"));
|
||||
labelPanel.add(new JLabel("Spectral Display Noise Floor"));
|
||||
|
||||
mLogIndexesButton = new JButton("Log Settings");
|
||||
mLogIndexesButton.addActionListener(e -> {
|
||||
@@ -340,6 +357,22 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the channel configuration squelch auto-track feature setting.
|
||||
* @param autoTrack true to enable.
|
||||
*/
|
||||
private void setConfigSquelchAutoTrack(boolean autoTrack)
|
||||
{
|
||||
Channel channel = mPlaylistManager.getChannelProcessingManager().getChannel(mProcessingChain);
|
||||
|
||||
if(channel != null && channel.getDecodeConfiguration() instanceof ISquelchConfiguration)
|
||||
{
|
||||
ISquelchConfiguration configuration = (ISquelchConfiguration)channel.getDecodeConfiguration();
|
||||
configuration.setSquelchAutoTrack(autoTrack);
|
||||
mPlaylistManager.schedulePlaylistSave();
|
||||
}
|
||||
}
|
||||
|
||||
private void broadcast(SourceEvent sourceEvent)
|
||||
{
|
||||
if(mProcessingChain != null)
|
||||
@@ -372,6 +405,8 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
mSquelchValueLabel.setEnabled(false);
|
||||
mSquelchUpButton.setEnabled(false);
|
||||
mSquelchDownButton.setEnabled(false);
|
||||
mSquelchAutoTrackCheckBox.setEnabled(false);
|
||||
mSquelchAutoTrackCheckBox.setSelected(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -420,7 +455,8 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
|
||||
updateFFTProcessing();
|
||||
|
||||
broadcast(SourceEvent.requestCurrentSquelchThreshold(null));
|
||||
broadcast(SourceEvent.requestCurrentSquelchAutoTrack());
|
||||
broadcast(SourceEvent.requestCurrentSquelchThreshold());
|
||||
}
|
||||
|
||||
|
||||
@@ -475,6 +511,15 @@ public class ChannelPowerPanel extends JPanel implements Listener<ProcessingChai
|
||||
updatePllFrequency(sourceEvent.getValue().longValue());
|
||||
}
|
||||
}
|
||||
case NOTIFICATION_SQUELCH_AUTO_TRACK ->
|
||||
{
|
||||
boolean autoTrack = sourceEvent.getValue().intValue() == 1 ? true : false;
|
||||
setConfigSquelchAutoTrack(autoTrack);
|
||||
EventQueue.invokeLater(() -> {
|
||||
mSquelchAutoTrackCheckBox.setSelected(autoTrack);
|
||||
mSquelchAutoTrackCheckBox.setEnabled(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user