mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Compare commits
14 Commits
v0.5.0-alp
...
v0.5.0-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4f3db7436 | ||
|
|
3ebfa90ca1 | ||
|
|
733fdd0d7a | ||
|
|
09893605eb | ||
|
|
57468ef932 | ||
|
|
c6afb1c133 | ||
|
|
05c0b51fde | ||
|
|
de8cfa24f9 | ||
|
|
fe714c69a7 | ||
|
|
ac219cf2ba | ||
|
|
77b1102100 | ||
|
|
5117e6cbe8 | ||
|
|
61c4e13c3e | ||
|
|
f1190b7be3 |
@@ -33,7 +33,7 @@ repositories {
|
||||
maven { url "https://jitpack.io" }
|
||||
}
|
||||
|
||||
version = '0.5.0-alpha4'
|
||||
version = '0.5.0-alpha5'
|
||||
sourceCompatibility = '13'
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -323,6 +323,11 @@ public class BroadcastifyCallBroadcaster extends AbstractAudioBroadcaster<Broadc
|
||||
audioRecording.removePendingReplay();
|
||||
}
|
||||
}
|
||||
else if(urlResponse.startsWith("1 SKIPPED"))
|
||||
{
|
||||
//Broadcastify is telling us to skip audio upload - someone already uploaded it
|
||||
audioRecording.removePendingReplay();
|
||||
}
|
||||
else
|
||||
{
|
||||
mLog.error("Broadcastify calls API upload URL request failed [" + urlResponse + "]");
|
||||
|
||||
@@ -25,7 +25,8 @@ import io.github.dsheirer.channel.state.State;
|
||||
import io.github.dsheirer.controller.channel.Channel;
|
||||
import io.github.dsheirer.controller.channel.ChannelModel;
|
||||
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
|
||||
import io.github.dsheirer.controller.channel.ChannelUtils;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.gui.playlist.channel.ViewChannelRequest;
|
||||
import io.github.dsheirer.icon.IconModel;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
||||
@@ -412,11 +413,12 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
||||
|
||||
if(channel != null)
|
||||
{
|
||||
popupMenu.add(ChannelUtils.getContextMenu(mChannelModel, mChannelProcessingManager, channel, mTable));
|
||||
JMenuItem viewChannel = new JMenuItem("View/Edit: " + channel.getShortTitle());
|
||||
viewChannel.addActionListener(e2 -> MyEventBus.getGlobalEventBus().post(new ViewChannelRequest(channel)));
|
||||
popupMenu.add(viewChannel);
|
||||
populated = true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -126,8 +126,6 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
mChannelID = UNIQUE_ID++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Creates a (new) deep copy of this channel
|
||||
*/
|
||||
@@ -183,6 +181,70 @@ public class Channel extends Configuration implements Listener<SourceEvent>
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a short title containing the system, site and channel name where each value is constrained to
|
||||
* ten characters each.
|
||||
* @return short title
|
||||
*/
|
||||
@JsonIgnore
|
||||
public String getShortTitle()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if(getSystem() != null)
|
||||
{
|
||||
if(getSystem().length() > 10)
|
||||
{
|
||||
sb.append(getSystem().substring(0, 10)).append("..");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(getSystem());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("No System");
|
||||
}
|
||||
|
||||
sb.append("/");
|
||||
|
||||
if(getSite() != null)
|
||||
{
|
||||
if(getSite().length() > 10)
|
||||
{
|
||||
sb.append(getSite().substring(0, 10)).append("..");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(getSite());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("No Site");
|
||||
}
|
||||
sb.append("/");
|
||||
|
||||
if(getName() != null)
|
||||
{
|
||||
if(getName().length() > 10)
|
||||
{
|
||||
sb.append(getName().substring(0, 10)).append("..");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append(getName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append("No Channel");
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the frequencies property as the source configuration changes or is initialized.
|
||||
*/
|
||||
|
||||
@@ -1,134 +0,0 @@
|
||||
/*
|
||||
* ******************************************************************************
|
||||
* sdrtrunk
|
||||
* Copyright (C) 2014-2018 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.controller.channel;
|
||||
|
||||
import io.github.dsheirer.channel.state.DecoderState;
|
||||
import io.github.dsheirer.module.ProcessingChain;
|
||||
import io.github.dsheirer.module.decode.event.ActivitySummaryFrame;
|
||||
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JSeparator;
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
public class ChannelUtils
|
||||
{
|
||||
/**
|
||||
* Creates a context menu for the channel argument
|
||||
*/
|
||||
public static JMenu getContextMenu(final ChannelModel channelModel,
|
||||
final ChannelProcessingManager channelProcessingManager,
|
||||
final Channel channel,
|
||||
final Component anchor)
|
||||
{
|
||||
if(channel != null)
|
||||
{
|
||||
JMenu menu = new JMenu("Channel: " + channel.getName());
|
||||
|
||||
if(channel.isProcessing())
|
||||
{
|
||||
JMenuItem disable = new JMenuItem("Disable");
|
||||
disable.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
channelProcessingManager.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_DISABLE));
|
||||
}
|
||||
});
|
||||
|
||||
menu.add(disable);
|
||||
|
||||
menu.add(new JSeparator());
|
||||
|
||||
JMenuItem actySummaryItem =
|
||||
new JMenuItem("Activity Summary");
|
||||
|
||||
actySummaryItem.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
ProcessingChain chain = channelProcessingManager.getProcessingChain(channel);
|
||||
|
||||
if(chain != null)
|
||||
{
|
||||
for(DecoderState decoderState : chain.getDecoderStates())
|
||||
{
|
||||
sb.append(decoderState.getActivitySummary());
|
||||
}
|
||||
}
|
||||
|
||||
new ActivitySummaryFrame(sb.toString(), anchor);
|
||||
}
|
||||
});
|
||||
|
||||
menu.add(actySummaryItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
JMenuItem enable = new JMenuItem("Enable");
|
||||
enable.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
channelProcessingManager.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_ENABLE));
|
||||
}
|
||||
});
|
||||
|
||||
menu.add(enable);
|
||||
}
|
||||
|
||||
menu.add(new JSeparator());
|
||||
|
||||
JMenuItem deleteItem = new JMenuItem("Delete");
|
||||
deleteItem.addActionListener(new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e)
|
||||
{
|
||||
int response = JOptionPane.showConfirmDialog(anchor,
|
||||
"Do you want to delete channel " + channel.getName() + "?",
|
||||
"Are you sure?", JOptionPane.YES_NO_CANCEL_OPTION);
|
||||
|
||||
if(response == JOptionPane.YES_OPTION)
|
||||
{
|
||||
channelModel.receive(new ChannelEvent(channel, ChannelEvent.Event.REQUEST_DELETE));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
menu.add(deleteItem);
|
||||
|
||||
return menu;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -56,7 +56,7 @@ public class ChannelStartProcessingRequest extends ModuleEventBusMessage
|
||||
{
|
||||
mChannel = channel;
|
||||
mChannelDescriptor = channelDescriptor;
|
||||
mIdentifierCollection = identifierCollection;
|
||||
mIdentifierCollection = (identifierCollection != null ? identifierCollection : new IdentifierCollection());
|
||||
mTrafficChannelManager = trafficChannelManager;
|
||||
}
|
||||
|
||||
|
||||
@@ -165,34 +165,25 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
|
||||
{
|
||||
PolyphaseChannelSource channelSource = null;
|
||||
|
||||
try
|
||||
List<Integer> polyphaseIndexes = mChannelCalculator.getChannelIndexes(tunerChannel);
|
||||
|
||||
IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes);
|
||||
|
||||
if(outputProcessor != null)
|
||||
{
|
||||
List<Integer> polyphaseIndexes = mChannelCalculator.getChannelIndexes(tunerChannel);
|
||||
long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes);
|
||||
|
||||
IPolyphaseChannelOutputProcessor outputProcessor = getOutputProcessor(polyphaseIndexes);
|
||||
|
||||
if(outputProcessor != null)
|
||||
try
|
||||
{
|
||||
long centerFrequency = mChannelCalculator.getCenterFrequencyForIndexes(polyphaseIndexes);
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
|
||||
mChannelCalculator.getChannelSampleRate(), centerFrequency, channelSpecification);
|
||||
|
||||
try
|
||||
{
|
||||
channelSource = new PolyphaseChannelSource(tunerChannel, outputProcessor, mChannelSourceEventListener,
|
||||
mChannelCalculator.getChannelSampleRate(), centerFrequency, channelSpecification);
|
||||
|
||||
mChannelSources.add(channelSource);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.debug("Couldn't design final output low pass filter for polyphase channel source");
|
||||
}
|
||||
mChannelSources.add(channelSource);
|
||||
}
|
||||
catch(FilterDesignException fde)
|
||||
{
|
||||
mLog.debug("Couldn't design final output low pass filter for polyphase channel source");
|
||||
}
|
||||
}
|
||||
catch(IllegalArgumentException iae)
|
||||
{
|
||||
mLog.info("Can't provide DDC for " + tunerChannel.toString() + " due to channelizer frequency [" +
|
||||
mChannelCalculator.getCenterFrequency() + "] and sample rate [" +
|
||||
(mChannelCalculator.getChannelCount() * mChannelCalculator.getChannelBandwidth()) + "]");
|
||||
}
|
||||
|
||||
return channelSource;
|
||||
|
||||
@@ -124,7 +124,7 @@ public class AliasEditor extends TabPane
|
||||
{
|
||||
if(mAliasViewByIdentifierEditor == null)
|
||||
{
|
||||
mAliasViewByIdentifierEditor = new AliasViewByIdentifierEditor(mPlaylistManager);
|
||||
mAliasViewByIdentifierEditor = new AliasViewByIdentifierEditor(mPlaylistManager, getAliasIdentifierTab().selectedProperty());
|
||||
}
|
||||
|
||||
return mAliasViewByIdentifierEditor;
|
||||
|
||||
@@ -25,11 +25,12 @@ import io.github.dsheirer.alias.id.AliasID;
|
||||
import io.github.dsheirer.alias.id.AliasIDType;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.playlist.PlaylistManager;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.property.ReadOnlyBooleanProperty;
|
||||
import javafx.beans.property.SimpleStringProperty;
|
||||
import javafx.beans.property.StringProperty;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.transformation.FilteredList;
|
||||
import javafx.geometry.HPos;
|
||||
import javafx.geometry.Insets;
|
||||
@@ -63,15 +64,19 @@ public class AliasViewByIdentifierEditor extends VBox
|
||||
private TableView<AliasAndIdentifier> mAliasAndIdentifierTableView;
|
||||
private TableColumn<AliasAndIdentifier,String> mIdentifierColumn;
|
||||
private Button mViewAliasButton;
|
||||
private boolean aliasListInvalidated;
|
||||
|
||||
public AliasViewByIdentifierEditor(PlaylistManager playlistManager)
|
||||
public AliasViewByIdentifierEditor(PlaylistManager playlistManager, ReadOnlyBooleanProperty tabSelectedProperty)
|
||||
{
|
||||
aliasListInvalidated = false;
|
||||
mPlaylistManager = playlistManager;
|
||||
mPlaylistManager.getAliasModel().aliasList().addListener((ListChangeListener<Alias>)c -> {
|
||||
//Refresh the list any time that the aliases change since we're using a static snapshot
|
||||
AliasIDType selected = getAliasIDTypeComboBox().getSelectionModel().getSelectedItem();
|
||||
getAliasIDTypeComboBox().getSelectionModel().select(null);
|
||||
getAliasIDTypeComboBox().getSelectionModel().select(selected);
|
||||
//Note if aliases change since we're using a static snapshot
|
||||
mPlaylistManager.getAliasModel().aliasList().addListener((InvalidationListener)observable -> aliasListInvalidated = true);
|
||||
|
||||
//Only update the list when this tab is selected to avoid many slow updates in the background
|
||||
tabSelectedProperty.addListener(observable -> {
|
||||
if (aliasListInvalidated)
|
||||
updateList();
|
||||
});
|
||||
|
||||
GridPane gridPane = new GridPane();
|
||||
@@ -131,6 +136,8 @@ public class AliasViewByIdentifierEditor extends VBox
|
||||
}
|
||||
|
||||
getAliasAndIdentifierTableView().getSortOrder().setAll(mIdentifierColumn);
|
||||
|
||||
aliasListInvalidated = false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,22 +197,22 @@ public class AliasViewByIdentifierEditor extends VBox
|
||||
{
|
||||
if(mAliasAndIdentifierTableView == null)
|
||||
{
|
||||
mAliasAndIdentifierTableView = new TableView(FXCollections.observableArrayList(AliasAndIdentifier.extractor()));
|
||||
mAliasAndIdentifierTableView = new TableView<>(FXCollections.observableArrayList(AliasAndIdentifier.extractor()));
|
||||
mAliasAndIdentifierTableView.setPlaceholder(new Label("No aliases or identifiers available"));
|
||||
mIdentifierColumn = new TableColumn<>("Identifier");
|
||||
mIdentifierColumn.setCellValueFactory(new PropertyValueFactory<>("identifier"));
|
||||
mIdentifierColumn.setPrefWidth(350);
|
||||
|
||||
TableColumn aliasColumn = new TableColumn();
|
||||
TableColumn<AliasAndIdentifier, String> aliasColumn = new TableColumn<>();
|
||||
aliasColumn.setText("Alias");
|
||||
aliasColumn.setCellValueFactory(new PropertyValueFactory<>("alias"));
|
||||
aliasColumn.setPrefWidth(200);
|
||||
|
||||
TableColumn groupColumn = new TableColumn();
|
||||
TableColumn<AliasAndIdentifier, String> groupColumn = new TableColumn<>();
|
||||
groupColumn.setText("Group");
|
||||
groupColumn.setCellValueFactory(new PropertyValueFactory<>("group"));
|
||||
groupColumn.setPrefWidth(200);
|
||||
|
||||
|
||||
mAliasAndIdentifierTableView.getColumns().addAll(mIdentifierColumn, aliasColumn, groupColumn);
|
||||
mAliasAndIdentifierTableView.setMaxHeight(Double.MAX_VALUE);
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), false);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel());
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -84,6 +84,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
private EventLogConfigurationEditor mEventLogConfigurationEditor;
|
||||
private RecordConfigurationEditor mRecordConfigurationEditor;
|
||||
private ToggleSwitch mIgnoreDataCallsButton;
|
||||
private ToggleSwitch mIgnoreCRCChecksumsButton;
|
||||
private Spinner<Integer> mTrafficChannelPoolSizeSpinner;
|
||||
private TableView<TimeslotFrequency> mTimeslotFrequencyTable;
|
||||
private IntegerTextField mLogicalSlotNumberField;
|
||||
@@ -149,17 +150,25 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
GridPane.setConstraints(getIgnoreDataCallsButton(), 2, row);
|
||||
gridPane.getChildren().add(getIgnoreDataCallsButton());
|
||||
|
||||
Label directionLabel = new Label("Ignore Data Calls");
|
||||
GridPane.setHalignment(directionLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(directionLabel, 3, row);
|
||||
gridPane.getChildren().add(directionLabel);
|
||||
Label ignoreDataLabel = new Label("Ignore Data Calls");
|
||||
GridPane.setHalignment(ignoreDataLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(ignoreDataLabel, 3, row);
|
||||
gridPane.getChildren().add(ignoreDataLabel);
|
||||
|
||||
GridPane.setConstraints(getIgnoreCRCChecksumsButton(), 4, row);
|
||||
gridPane.getChildren().add(getIgnoreCRCChecksumsButton());
|
||||
|
||||
Label ignoreCRCLabel = new Label("Ignore CRC Checksums (RAS)");
|
||||
GridPane.setHalignment(ignoreCRCLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(ignoreCRCLabel, 5, row);
|
||||
gridPane.getChildren().add(ignoreCRCLabel);
|
||||
|
||||
Label timeslotTableLabel = new Label("Logical Slot Number (LSN) to Frequency Map. Required for: Connect Plus and Tier-III systems that don't use absolute frequencies");
|
||||
GridPane.setHalignment(timeslotTableLabel, HPos.LEFT);
|
||||
GridPane.setConstraints(timeslotTableLabel, 0, ++row, 4, 1);
|
||||
GridPane.setConstraints(timeslotTableLabel, 0, ++row, 6, 1);
|
||||
gridPane.getChildren().add(timeslotTableLabel);
|
||||
|
||||
GridPane.setConstraints(getTimeslotTable(), 0, ++row, 4, 3);
|
||||
GridPane.setConstraints(getTimeslotTable(), 0, ++row, 6, 3);
|
||||
gridPane.getChildren().add(getTimeslotTable());
|
||||
|
||||
VBox buttonsBox = new VBox();
|
||||
@@ -167,7 +176,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
buttonsBox.setSpacing(10);
|
||||
buttonsBox.getChildren().addAll(getAddTimeslotFrequencyButton(), getDeleteTimeslotFrequencyButton());
|
||||
|
||||
GridPane.setConstraints(buttonsBox, 4, row, 1, 3);
|
||||
GridPane.setConstraints(buttonsBox, 6, row, 1, 3);
|
||||
gridPane.getChildren().addAll(buttonsBox);
|
||||
|
||||
row += 3;
|
||||
@@ -231,7 +240,10 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), true);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(),
|
||||
DecodeConfigDMR.CHANNEL_ROTATION_DELAY_MINIMUM_MS,
|
||||
DecodeConfigDMR.CHANNEL_ROTATION_DELAY_MAXIMUM_MS,
|
||||
DecodeConfigDMR.CHANNEL_ROTATION_DELAY_DEFAULT_MS);
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
@@ -486,6 +498,19 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
return mIgnoreDataCallsButton;
|
||||
}
|
||||
|
||||
private ToggleSwitch getIgnoreCRCChecksumsButton()
|
||||
{
|
||||
if(mIgnoreCRCChecksumsButton == null)
|
||||
{
|
||||
mIgnoreCRCChecksumsButton = new ToggleSwitch();
|
||||
mIgnoreCRCChecksumsButton.setDisable(true);
|
||||
mIgnoreCRCChecksumsButton.selectedProperty()
|
||||
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
}
|
||||
|
||||
return mIgnoreCRCChecksumsButton;
|
||||
}
|
||||
|
||||
private Spinner<Integer> getTrafficChannelPoolSizeSpinner()
|
||||
{
|
||||
if(mTrafficChannelPoolSizeSpinner == null)
|
||||
@@ -550,6 +575,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
@Override
|
||||
protected void setDecoderConfiguration(DecodeConfiguration config)
|
||||
{
|
||||
getIgnoreCRCChecksumsButton().setDisable(config == null);
|
||||
getIgnoreDataCallsButton().setDisable(config == null);
|
||||
getTrafficChannelPoolSizeSpinner().setDisable(config == null);
|
||||
getTimeslotTable().getItems().clear();
|
||||
@@ -569,6 +595,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
DecodeConfigDMR decodeConfig = (DecodeConfigDMR)config;
|
||||
|
||||
getIgnoreDataCallsButton().setSelected(decodeConfig.getIgnoreDataCalls());
|
||||
getIgnoreCRCChecksumsButton().setSelected(decodeConfig.getIgnoreCRCChecksums());
|
||||
getTrafficChannelPoolSizeSpinner().getValueFactory().setValue(decodeConfig.getTrafficChannelPoolSize());
|
||||
|
||||
for(TimeslotFrequency timeslotFrequency: decodeConfig.getTimeslotMap())
|
||||
@@ -578,6 +605,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
}
|
||||
else
|
||||
{
|
||||
getIgnoreCRCChecksumsButton().setSelected(false);
|
||||
getIgnoreDataCallsButton().setSelected(false);
|
||||
getTrafficChannelPoolSizeSpinner().getValueFactory().setValue(0);
|
||||
getChannelRotationDelaySpinner().getValueFactory().setValue(200);
|
||||
@@ -599,6 +627,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
|
||||
}
|
||||
|
||||
// config.setChannelRotationDelay(getChannelRotationDelaySpinner().getValue());
|
||||
config.setIgnoreCRCChecksums(getIgnoreCRCChecksumsButton().isSelected());
|
||||
config.setIgnoreDataCalls(getIgnoreDataCallsButton().isSelected());
|
||||
config.setTrafficChannelPoolSize(getTrafficChannelPoolSizeSpinner().getValue());
|
||||
config.setTimeslotMap(new ArrayList<>(getTimeslotTable().getItems()));
|
||||
|
||||
@@ -210,7 +210,7 @@ public class LTRConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), false);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel());
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -210,7 +210,7 @@ public class LTRNetConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), false);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel());
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -195,7 +195,10 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), true);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(),
|
||||
DecodeConfigMPT1327.CHANNEL_ROTATION_DELAY_MINIMUM_MS,
|
||||
DecodeConfigMPT1327.CHANNEL_ROTATION_DELAY_MAXIMUM_MS,
|
||||
DecodeConfigMPT1327.CHANNEL_ROTATION_DELAY_DEFAULT_MS);
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -200,7 +200,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), false);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel());
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -191,7 +191,10 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), true);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(),
|
||||
DecodeConfigP25Phase1.CHANNEL_ROTATION_DELAY_MINIMUM_MS,
|
||||
DecodeConfigP25Phase1.CHANNEL_ROTATION_DELAY_MAXIMUM_MS,
|
||||
DecodeConfigP25Phase1.CHANNEL_ROTATION_DELAY_DEFAULT_MS);
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -179,7 +179,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), false);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel());
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
@@ -305,7 +305,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
|
||||
config.setAutoDetectScrambleParameters(false);
|
||||
int wacn = getWacnTextField().get();
|
||||
int system = getSystemTextField().get();
|
||||
int nac = getSystemTextField().get();
|
||||
int nac = getNacTextField().get();
|
||||
config.setScrambleParameters(new ScrambleParameters(wacn, system, nac));
|
||||
}
|
||||
else
|
||||
|
||||
@@ -155,7 +155,7 @@ public class PassportConfigurationEditor extends ChannelConfigurationEditor
|
||||
{
|
||||
if(mSourceConfigurationEditor == null)
|
||||
{
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel(), false);
|
||||
mSourceConfigurationEditor = new FrequencyEditor(getTunerModel());
|
||||
|
||||
//Add a listener so that we can push change notifications up to this editor
|
||||
mSourceConfigurationEditor.modifiedProperty()
|
||||
|
||||
@@ -65,19 +65,42 @@ public class FrequencyEditor extends SourceConfigurationEditor<SourceConfigurati
|
||||
private ComboBox<String> mPreferredTunerComboBox;
|
||||
private VBox mFrequencyBoxContainer;
|
||||
private Spinner<Integer> mChannelRotationDelaySpinner;
|
||||
private boolean mAllowMultipleFrequencies;
|
||||
private boolean mAllowMultipleFrequencies = false;
|
||||
private int mFrequencyRotationDefault = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_DEFAULT;
|
||||
private int mFrequencyRotationMinimum = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MINIMUM;
|
||||
private int mFrequencyRotationMaximum = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MAXIMUM;
|
||||
|
||||
/**
|
||||
* Constructs an instance.
|
||||
* Constructs an instance with support for multiple frequency values. The min and max rotation delay values
|
||||
* define constraints for the channel rotation delay monitor, how long the monitor dwells on each frequency in a
|
||||
* multi-frequency configuration.
|
||||
*
|
||||
* @param tunerModel for accessing list of tuners to select a preferred tuner
|
||||
* @param allowMultipleFrequencies toggles behavior of the editor between single frequency (false) or multiple
|
||||
* frequencies (true).
|
||||
* @param minRotationDelay in milliseconds for channel/frequency dwell
|
||||
* @param maxRotationDelay in milliseconds for channel/frequency dwell
|
||||
*/
|
||||
public FrequencyEditor(TunerModel tunerModel, boolean allowMultipleFrequencies)
|
||||
public FrequencyEditor(TunerModel tunerModel, int minRotationDelay, int maxRotationDelay, int defaultRotationDelay)
|
||||
{
|
||||
mTunerModel = tunerModel;
|
||||
mAllowMultipleFrequencies = allowMultipleFrequencies;
|
||||
mAllowMultipleFrequencies = true;
|
||||
mFrequencyRotationMinimum = minRotationDelay;
|
||||
mFrequencyRotationMaximum = maxRotationDelay;
|
||||
mFrequencyRotationDefault = defaultRotationDelay;
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an instance with a default behavior of single frequency support only.
|
||||
* @param tunerModel for accessing list of tuners to select a preferred tuner
|
||||
*/
|
||||
public FrequencyEditor(TunerModel tunerModel)
|
||||
{
|
||||
mTunerModel = tunerModel;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init()
|
||||
{
|
||||
HBox hBox = new HBox();
|
||||
hBox.setPadding(new Insets(10,10,10,10));
|
||||
hBox.setSpacing(10);
|
||||
@@ -93,17 +116,25 @@ public class FrequencyEditor extends SourceConfigurationEditor<SourceConfigurati
|
||||
tunerBox.setSpacing(10);
|
||||
tunerBox.getChildren().addAll(preferredTunerLabel, getPreferredTunerComboBox());
|
||||
|
||||
Label frequencyRotationLabel = new Label("Frequency Rotation Delay (ms)");
|
||||
HBox frequencyBox = new HBox();
|
||||
frequencyBox.setAlignment(Pos.CENTER_LEFT);
|
||||
frequencyBox.setSpacing(10);
|
||||
frequencyBox.getChildren().addAll(frequencyRotationLabel, getFrequencyRotationDelaySpinner());
|
||||
getFrequencyRotationDelaySpinner().disableProperty().bind(Bindings.greaterThan(2, Bindings.size(mFrequencyBoxes)));
|
||||
|
||||
VBox vbox = new VBox();
|
||||
vbox.setSpacing(10);
|
||||
vbox.setPadding(new Insets(0,0,0,20));
|
||||
vbox.getChildren().addAll(tunerBox, frequencyBox);
|
||||
|
||||
if(mAllowMultipleFrequencies)
|
||||
{
|
||||
Label frequencyRotationLabel = new Label("Frequency Rotation Delay (ms)");
|
||||
HBox frequencyBox = new HBox();
|
||||
frequencyBox.setAlignment(Pos.CENTER_LEFT);
|
||||
frequencyBox.setSpacing(10);
|
||||
frequencyBox.getChildren().addAll(frequencyRotationLabel, getFrequencyRotationDelaySpinner());
|
||||
getFrequencyRotationDelaySpinner().disableProperty().bind(Bindings.greaterThan(2, Bindings.size(mFrequencyBoxes)));
|
||||
|
||||
vbox.getChildren().addAll(tunerBox, frequencyBox);
|
||||
}
|
||||
else
|
||||
{
|
||||
vbox.getChildren().addAll(tunerBox);
|
||||
}
|
||||
|
||||
hBox.getChildren().add(vbox);
|
||||
|
||||
@@ -225,7 +256,25 @@ public class FrequencyEditor extends SourceConfigurationEditor<SourceConfigurati
|
||||
SourceConfigTunerMultipleFrequency sourceMulti = (SourceConfigTunerMultipleFrequency)sourceConfiguration;
|
||||
setFrequencies(sourceMulti.getFrequencies());
|
||||
preferredTuner = sourceMulti.getPreferredTuner();
|
||||
getFrequencyRotationDelaySpinner().getValueFactory().setValue(sourceMulti.getFrequencyRotationDelay());
|
||||
|
||||
int rotationDelay = sourceMulti.getFrequencyRotationDelay();
|
||||
|
||||
if(rotationDelay < mFrequencyRotationMinimum)
|
||||
{
|
||||
rotationDelay = mFrequencyRotationMinimum;
|
||||
|
||||
//For backward compatibility, we'll automatically update the value in the source config
|
||||
sourceMulti.setFrequencyRotationDelay(rotationDelay);
|
||||
}
|
||||
if(rotationDelay > mFrequencyRotationMaximum)
|
||||
{
|
||||
rotationDelay = mFrequencyRotationMaximum;
|
||||
|
||||
//For backward compatibility, we'll automatically update the value in the source config
|
||||
sourceMulti.setFrequencyRotationDelay(rotationDelay);
|
||||
}
|
||||
|
||||
getFrequencyRotationDelaySpinner().getValueFactory().setValue(rotationDelay);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -236,7 +285,6 @@ public class FrequencyEditor extends SourceConfigurationEditor<SourceConfigurati
|
||||
|
||||
updatePreferredTuners();
|
||||
|
||||
|
||||
if(preferredTuner != null)
|
||||
{
|
||||
if(!getPreferredTunerComboBox().getItems().contains(preferredTuner))
|
||||
@@ -267,9 +315,8 @@ public class FrequencyEditor extends SourceConfigurationEditor<SourceConfigurati
|
||||
mChannelRotationDelaySpinner.setTooltip(
|
||||
new Tooltip("Delay on each frequency before rotating to next when seeking to next active channel frequency"));
|
||||
mChannelRotationDelaySpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL);
|
||||
int min = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MINIMUM;
|
||||
int max = ChannelRotationMonitor.CHANNEL_ROTATION_DELAY_MAXIMUM;
|
||||
SpinnerValueFactory<Integer> svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(min, max, min, 50);
|
||||
SpinnerValueFactory<Integer> svf = new SpinnerValueFactory.IntegerSpinnerValueFactory(mFrequencyRotationMinimum,
|
||||
mFrequencyRotationMaximum, mFrequencyRotationMinimum, 50);
|
||||
mChannelRotationDelaySpinner.setValueFactory(svf);
|
||||
mChannelRotationDelaySpinner.getValueFactory().valueProperty()
|
||||
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
|
||||
@@ -278,7 +325,6 @@ public class FrequencyEditor extends SourceConfigurationEditor<SourceConfigurati
|
||||
return mChannelRotationDelaySpinner;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the preferred tuner's combo box to reflect the current set of tuners.
|
||||
*/
|
||||
|
||||
@@ -74,7 +74,6 @@ import io.github.dsheirer.module.decode.dmr.message.voice.VoiceMessage;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventType;
|
||||
import io.github.dsheirer.source.tuner.channel.rotation.AddChannelRotationActiveStateRequest;
|
||||
import org.apache.commons.math3.util.FastMath;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -95,15 +94,16 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
private DMRTrafficChannelManager mTrafficChannelManager;
|
||||
private DecodeEvent mCurrentCallEvent;
|
||||
private long mCurrentFrequency;
|
||||
private Map<Long,DecodeEvent> mDetectedCallEventsMap = new TreeMap<>();
|
||||
private boolean mIgnoreCRCChecksums;
|
||||
private Map<Integer,DecodeEvent> mDetectedCallEventsMap = new TreeMap<>();
|
||||
private static final AddChannelRotationActiveStateRequest CAPACITY_PLUS_ACTIVE_STATE_REQUEST =
|
||||
new AddChannelRotationActiveStateRequest(State.ACTIVE);
|
||||
|
||||
/**
|
||||
* Constructs an DMR decoder state with an optional traffic channel manager.
|
||||
* @param channel with configuration details
|
||||
* @param timeslot for this decoder state (1 or 2)
|
||||
* @param trafficChannelManager for handling traffic channel grants.
|
||||
* @param configurationMonitor for tracking activity summary
|
||||
*/
|
||||
public DMRDecoderState(Channel channel, int timeslot, DMRTrafficChannelManager trafficChannelManager)
|
||||
{
|
||||
@@ -111,12 +111,18 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
mChannel = channel;
|
||||
mTrafficChannelManager = trafficChannelManager;
|
||||
|
||||
//The decoder state passes all messages to the network configuration monitor, so we only contruct
|
||||
//The decoder state passes all messages to the network configuration monitor, so we only construct
|
||||
//the monitor for timeslot 1.
|
||||
if(timeslot == 1)
|
||||
{
|
||||
mNetworkConfigurationMonitor = new DMRNetworkConfigurationMonitor(channel);
|
||||
}
|
||||
|
||||
//For RAS protected systems, allows user to ignore CRC checksums and still decode the system
|
||||
if(channel.getDecodeConfiguration() instanceof DecodeConfigDMR)
|
||||
{
|
||||
mIgnoreCRCChecksums = ((DecodeConfigDMR)channel.getDecodeConfiguration()).getIgnoreCRCChecksums();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -181,7 +187,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
@Override
|
||||
public void receive(IMessage message)
|
||||
{
|
||||
if(message.isValid() && message.getTimeslot() == getTimeslot())
|
||||
if(message.getTimeslot() == getTimeslot())
|
||||
{
|
||||
if(message instanceof VoiceMessage)
|
||||
{
|
||||
@@ -191,11 +197,11 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
processData((DataMessage)message);
|
||||
}
|
||||
else if(message instanceof LCMessage)
|
||||
else if((message.isValid() || mIgnoreCRCChecksums) && message instanceof LCMessage)
|
||||
{
|
||||
processLinkControl((LCMessage)message, false);
|
||||
}
|
||||
else if(message instanceof DMRPacketMessage)
|
||||
else if(message.isValid() && message instanceof DMRPacketMessage)
|
||||
{
|
||||
processPacket((DMRPacketMessage)message);
|
||||
}
|
||||
@@ -205,13 +211,14 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
}
|
||||
}
|
||||
//SLCO messages on timeslot 0 to catch capacity plus rest channel events
|
||||
else if(message.isValid() && message.getTimeslot() == 0 && message instanceof LCMessage)
|
||||
else if((message.isValid() || mIgnoreCRCChecksums) && message.getTimeslot() == 0 && message instanceof LCMessage)
|
||||
{
|
||||
processLinkControl((LCMessage)message, false);
|
||||
}
|
||||
|
||||
//Pass the message to the network configuration monitor, if this decoder state has a non-null instance
|
||||
if(mNetworkConfigurationMonitor != null && message.isValid() && message instanceof DMRMessage)
|
||||
if(mNetworkConfigurationMonitor != null && (message.isValid() || mIgnoreCRCChecksums) &&
|
||||
message instanceof DMRMessage)
|
||||
{
|
||||
mNetworkConfigurationMonitor.process((DMRMessage)message);
|
||||
}
|
||||
@@ -265,7 +272,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
*/
|
||||
private void processPacket(DMRPacketMessage packet)
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA, getTimeslot()));
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
|
||||
|
||||
DecodeEvent packetEvent = DMRDecodeEvent.builder(packet.getTimestamp())
|
||||
.eventDescription(DecodeEventType.DATA_PACKET.name())
|
||||
@@ -302,7 +309,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
case MBC_ENC_HEADER:
|
||||
case DATA_ENC_HEADER:
|
||||
case CHANNEL_CONTROL_ENC_HEADER:
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.DATA, getTimeslot()));
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -317,13 +324,16 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
|
||||
/**
|
||||
* Process Data Messages
|
||||
*
|
||||
* Note: invalid messages are allowed to pass to this method. Messages are selectively checked for isValid()
|
||||
* to overcome RAS implementation in certain systems.
|
||||
*/
|
||||
private void processData(DataMessage message)
|
||||
{
|
||||
switch(message.getSlotType().getDataType())
|
||||
{
|
||||
case CSBK:
|
||||
if(message instanceof CSBKMessage)
|
||||
if((message.isValid() || mIgnoreCRCChecksums) && message instanceof CSBKMessage)
|
||||
{
|
||||
processCSBK((CSBKMessage)message);
|
||||
}
|
||||
@@ -358,10 +368,12 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
processTerminator((Terminator)message);
|
||||
}
|
||||
break;
|
||||
case MBC_BLOCK:
|
||||
case RATE_1_OF_2_DATA:
|
||||
case RATE_3_OF_4_DATA:
|
||||
case RATE_1_DATA:
|
||||
broadcast(new DecoderStateEvent(this, Event.START, State.DATA, getTimeslot()));
|
||||
break;
|
||||
case MBC_BLOCK:
|
||||
case RESERVED_15:
|
||||
case UNKNOWN:
|
||||
default:
|
||||
@@ -408,7 +420,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(csbk instanceof Acknowledge)
|
||||
{
|
||||
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.RESPONSE.name())
|
||||
.eventDescription(DecodeEventType.RESPONSE.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details(((Acknowledge)csbk).getReason().toString())
|
||||
@@ -423,7 +435,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(csbk instanceof Acknowledge)
|
||||
{
|
||||
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.RESPONSE.name())
|
||||
.eventDescription(DecodeEventType.RESPONSE.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details(((Acknowledge)csbk).getReason().toString())
|
||||
@@ -440,7 +452,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
case AUTHENTICATE_REGISTER_RADIO_CHECK_SERVICE:
|
||||
DecodeEvent registerEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.COMMAND.name())
|
||||
.eventDescription(DecodeEventType.COMMAND.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details(DecodeEventType.REGISTER.toString())
|
||||
@@ -449,7 +461,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
break;
|
||||
case CANCEL_CALL_SERVICE:
|
||||
DecodeEvent cancelEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.COMMAND.name())
|
||||
.eventDescription(DecodeEventType.COMMAND.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details("CANCEL CALL")
|
||||
@@ -460,7 +472,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(csbk instanceof StunReviveKill)
|
||||
{
|
||||
DecodeEvent stunEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.COMMAND.name())
|
||||
.eventDescription(DecodeEventType.COMMAND.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details(((StunReviveKill)csbk).getCommand() + " RADIO")
|
||||
@@ -481,7 +493,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
ServiceRadioCheck src = (ServiceRadioCheck)csbk;
|
||||
|
||||
DecodeEvent checkEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.RADIO_CHECK.name())
|
||||
.eventDescription(DecodeEventType.RADIO_CHECK.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details(src.getServiceDescription() + " SERVICE FOR " +
|
||||
@@ -502,7 +514,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(aloha.hasRadioIdentifier())
|
||||
{
|
||||
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.RESPONSE.name())
|
||||
.eventDescription(DecodeEventType.RESPONSE.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details("Aloha Acknowledge")
|
||||
@@ -521,7 +533,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
case MASS_REGISTRATION:
|
||||
DecodeEvent massEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.REGISTER.name())
|
||||
.eventDescription(DecodeEventType.REGISTER.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details("MASS REGISTRATION")
|
||||
@@ -532,7 +544,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(csbk instanceof VoteNowAdvice)
|
||||
{
|
||||
DecodeEvent voteEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.COMMAND.name())
|
||||
.eventDescription(DecodeEventType.COMMAND.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details("VOTE NOW FOR " + ((VoteNowAdvice)csbk).getVotedSystemIdentityCode())
|
||||
@@ -556,7 +568,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(csbk instanceof Protect)
|
||||
{
|
||||
DecodeEvent protectEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.COMMAND.name())
|
||||
.eventDescription(DecodeEventType.COMMAND.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details("PROTECT: " + ((Protect)csbk).getProtectKind())
|
||||
@@ -602,12 +614,37 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
ChannelGrant dataGrant = (ChannelGrant)csbk;
|
||||
DMRChannel channel = dataGrant.getChannel();
|
||||
|
||||
IdentifierCollection mergedIdentifiers = getMergedIdentifierCollection(csbk.getIdentifiers());
|
||||
|
||||
if(hasTrafficChannelManager())
|
||||
{
|
||||
mTrafficChannelManager.processChannelGrant(channel, getMergedIdentifierCollection(csbk.getIdentifiers()),
|
||||
csbk.getOpcode(), csbk.getTimestamp(), csbk.isEncrypted());
|
||||
mTrafficChannelManager.processChannelGrant(channel, mergedIdentifiers, csbk.getOpcode(),
|
||||
csbk.getTimestamp(), csbk.isEncrypted());
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
|
||||
|
||||
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
|
||||
{
|
||||
event = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.channel(channel)
|
||||
.details(csbk.getOpcode().getLabel())
|
||||
.eventDescription(DecodeEventType.DATA_CALL.toString())
|
||||
.identifiers(mergedIdentifiers)
|
||||
.timeslot(channel.getTimeslot())
|
||||
.build();
|
||||
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the ending timestamp for the event and rebroadcast
|
||||
event.end(csbk.getTimestamp());
|
||||
}
|
||||
|
||||
broadcast(event);
|
||||
}
|
||||
processCallDetection(dataGrant.getChannel(), dataGrant.getIdentifiers(), dataGrant.getTimestamp(), DecodeEventType.DATA_CALL);
|
||||
}
|
||||
break;
|
||||
case STANDARD_BROADCAST_TALKGROUP_VOICE_CHANNEL_GRANT:
|
||||
@@ -616,12 +653,37 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
ChannelGrant tgGrant = (ChannelGrant)csbk;
|
||||
DMRChannel channel = tgGrant.getChannel();
|
||||
|
||||
IdentifierCollection mergedIdentifiers = getMergedIdentifierCollection(csbk.getIdentifiers());
|
||||
|
||||
if(hasTrafficChannelManager())
|
||||
{
|
||||
mTrafficChannelManager.processChannelGrant(channel, getMergedIdentifierCollection(csbk.getIdentifiers()),
|
||||
mTrafficChannelManager.processChannelGrant(channel, mergedIdentifiers,
|
||||
csbk.getOpcode(), csbk.getTimestamp(), csbk.isEncrypted());
|
||||
}
|
||||
processCallDetection(tgGrant.getChannel(), tgGrant.getIdentifiers(), tgGrant.getTimestamp(), DecodeEventType.CALL_GROUP);
|
||||
else
|
||||
{
|
||||
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
|
||||
|
||||
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
|
||||
{
|
||||
event = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.channel(channel)
|
||||
.details(csbk.getOpcode().getLabel())
|
||||
.eventDescription(DecodeEventType.CALL_GROUP.toString())
|
||||
.identifiers(mergedIdentifiers)
|
||||
.timeslot(channel.getTimeslot())
|
||||
.build();
|
||||
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the ending timestamp for the event and rebroadcast
|
||||
event.end(csbk.getTimestamp());
|
||||
}
|
||||
|
||||
broadcast(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case STANDARD_DUPLEX_PRIVATE_VOICE_CHANNEL_GRANT:
|
||||
@@ -630,13 +692,36 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
{
|
||||
ChannelGrant channelGrant = (ChannelGrant)csbk;
|
||||
DMRChannel channel = channelGrant.getChannel();
|
||||
IdentifierCollection mergedIdentifiers = getMergedIdentifierCollection(csbk.getIdentifiers());
|
||||
|
||||
if(hasTrafficChannelManager())
|
||||
{
|
||||
mTrafficChannelManager.processChannelGrant(channel, getMergedIdentifierCollection(csbk.getIdentifiers()),
|
||||
mTrafficChannelManager.processChannelGrant(channel, mergedIdentifiers,
|
||||
csbk.getOpcode(), csbk.getTimestamp(), csbk.isEncrypted());
|
||||
}
|
||||
processCallDetection(channelGrant.getChannel(), channelGrant.getIdentifiers(),
|
||||
channelGrant.getTimestamp(), DecodeEventType.CALL_UNIT_TO_UNIT);
|
||||
else
|
||||
{
|
||||
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
|
||||
|
||||
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
|
||||
{
|
||||
event = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.channel(channel)
|
||||
.details(csbk.getOpcode().getLabel())
|
||||
.eventDescription(DecodeEventType.CALL_UNIT_TO_UNIT.toString())
|
||||
.identifiers(mergedIdentifiers)
|
||||
.timeslot(channel.getTimeslot())
|
||||
.build();
|
||||
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the ending timestamp for the event and rebroadcast
|
||||
event.end(csbk.getTimestamp());
|
||||
}
|
||||
|
||||
broadcast(event);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MOTOROLA_CAPMAX_ALOHA:
|
||||
@@ -647,7 +732,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(cmAloha.hasRadioIdentifier())
|
||||
{
|
||||
DecodeEvent ackEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.eventDescription(DecodeEventType.RESPONSE.name())
|
||||
.eventDescription(DecodeEventType.RESPONSE.toString())
|
||||
.identifiers(getMergedIdentifierCollection(csbk.getIdentifiers()))
|
||||
.timeslot(csbk.getTimeslot())
|
||||
.details("Aloha Acknowledge")
|
||||
@@ -664,13 +749,38 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
if(csbk instanceof ConnectPlusDataChannelGrant)
|
||||
{
|
||||
ConnectPlusDataChannelGrant cpdcg = (ConnectPlusDataChannelGrant)csbk;
|
||||
DMRChannel channel = cpdcg.getChannel();
|
||||
|
||||
IdentifierCollection mergedIdentifiers = getMergedIdentifierCollection(csbk.getIdentifiers());
|
||||
|
||||
if(hasTrafficChannelManager())
|
||||
{
|
||||
mTrafficChannelManager.processChannelGrant(cpdcg.getChannel(),
|
||||
getMergedIdentifierCollection(csbk.getIdentifiers()), csbk.getOpcode(), csbk.getTimestamp(), csbk.isEncrypted());
|
||||
mTrafficChannelManager.processChannelGrant(channel, mergedIdentifiers, csbk.getOpcode(),
|
||||
csbk.getTimestamp(), csbk.isEncrypted());
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
|
||||
|
||||
if(isStale(event, csbk.getTimestamp(), csbk.getIdentifiers()))
|
||||
{
|
||||
event = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.channel(channel)
|
||||
.details(csbk.getOpcode().getLabel())
|
||||
.eventDescription(DecodeEventType.DATA_CALL.toString())
|
||||
.identifiers(mergedIdentifiers)
|
||||
.timeslot(channel.getTimeslot())
|
||||
.build();
|
||||
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the ending timestamp for the event and rebroadcast
|
||||
event.end(csbk.getTimestamp());
|
||||
}
|
||||
|
||||
broadcast(event);
|
||||
}
|
||||
processCallDetection(cpdcg.getChannel(), cpdcg.getIdentifiers(), cpdcg.getTimestamp(),
|
||||
DecodeEventType.DATA_CALL);
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||
break;
|
||||
@@ -697,14 +807,39 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
case MOTOROLA_CONPLUS_VOICE_CHANNEL_USER:
|
||||
if(csbk instanceof ConnectPlusVoiceChannelUser)
|
||||
{
|
||||
DMRChannel channel = ((ConnectPlusVoiceChannelUser)csbk).getChannel();
|
||||
ConnectPlusVoiceChannelUser cpvcu = (ConnectPlusVoiceChannelUser)csbk;
|
||||
DMRChannel channel = cpvcu.getChannel();
|
||||
|
||||
IdentifierCollection mergedIdentifiers = getMergedIdentifierCollection(csbk.getIdentifiers());
|
||||
|
||||
if(hasTrafficChannelManager())
|
||||
{
|
||||
mTrafficChannelManager.processChannelGrant(channel, getMergedIdentifierCollection(csbk.getIdentifiers()),
|
||||
csbk.getOpcode(), csbk.getTimestamp(), csbk.isEncrypted());
|
||||
mTrafficChannelManager.processChannelGrant(channel, mergedIdentifiers, csbk.getOpcode(),
|
||||
csbk.getTimestamp(), csbk.isEncrypted());
|
||||
}
|
||||
else
|
||||
{
|
||||
DecodeEvent detectedEvent = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
|
||||
|
||||
if(isStale(detectedEvent, csbk.getTimestamp(), csbk.getIdentifiers()))
|
||||
{
|
||||
detectedEvent = DMRDecodeEvent.builder(csbk.getTimestamp())
|
||||
.channel(channel)
|
||||
.details(csbk.getOpcode().getLabel())
|
||||
.eventDescription(DecodeEventType.CALL_GROUP.toString())
|
||||
.identifiers(mergedIdentifiers)
|
||||
.timeslot(channel.getTimeslot())
|
||||
.build();
|
||||
mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), detectedEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Update the ending timestamp for the event and rebroadcast
|
||||
detectedEvent.end(csbk.getTimestamp());
|
||||
}
|
||||
|
||||
broadcast(detectedEvent);
|
||||
}
|
||||
processCallDetection(channel, csbk.getIdentifiers(), csbk.getTimestamp(),
|
||||
DecodeEventType.CALL_GROUP);
|
||||
}
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||
break;
|
||||
@@ -718,9 +853,6 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
broadcast(affiliateEvent);
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CONTROL, getTimeslot()));
|
||||
break;
|
||||
case STANDARD_MOVE_TSCC:
|
||||
|
||||
|
||||
default:
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ACTIVE, getTimeslot()));
|
||||
break;
|
||||
@@ -742,68 +874,22 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
}
|
||||
|
||||
/**
|
||||
* Process Call Detections.
|
||||
* Indicates if the event is a stale event, meaning that the event is null, or the event start exceeds the max
|
||||
* valid call duration threshold, or if the event identifiers don't match the current identifiers.
|
||||
*
|
||||
* Note: once full support for Connect+ trunk tracking is implemented with the DMRTrafficChannelManager, this will
|
||||
* have to be modified to detect if the trunked channel was allocated or if the call was simply detected. This
|
||||
* would be contingent on having a map of Logical Slot Numbers to frequency for the traffic channel manager to make
|
||||
* an allocation.
|
||||
*
|
||||
* @param channel for the call event
|
||||
* @param identifiers for the call event
|
||||
* @param timestamp of the event or update
|
||||
* @param event to check for staleness
|
||||
* @param timestamp to check the event against
|
||||
* @param currentIdentifiers to compare against the event
|
||||
* @return true if the event is stale.
|
||||
*/
|
||||
private void processCallDetection(DMRChannel channel, List<Identifier> identifiers, long timestamp,
|
||||
DecodeEventType eventType)
|
||||
private boolean isStale(DecodeEvent event, long timestamp, List<Identifier> currentIdentifiers)
|
||||
{
|
||||
//Check to see if there is a current call event to see if the detected call event is actually for this timeslot
|
||||
//and we can then identify the LSN for this timeslot
|
||||
if(mCurrentFrequency == 0 && mCurrentCallEvent != null &&
|
||||
isSameCall(mCurrentCallEvent.getIdentifierCollection(), identifiers))
|
||||
if(event == null || (timestamp - event.getTimeStart() > MAX_VALID_CALL_DURATION_MS))
|
||||
{
|
||||
mCurrentFrequency = channel.getDownlinkFrequency();
|
||||
|
||||
if(mNetworkConfigurationMonitor != null)
|
||||
{
|
||||
mNetworkConfigurationMonitor.setCurrentChannel(channel);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DecodeEvent event = mDetectedCallEventsMap.get(channel.getLogicalSlotNumber());
|
||||
|
||||
if(event == null)
|
||||
{
|
||||
event = DMRDecodeEvent.builder(timestamp)
|
||||
.timeslot(getTimeslot())
|
||||
.identifiers(getMergedIdentifierCollection(identifiers))
|
||||
.eventDescription(eventType.toString())
|
||||
.build();
|
||||
// mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event.getIdentifierCollection() != null &&
|
||||
isSameCall(event.getIdentifierCollection(), identifiers) &&
|
||||
FastMath.abs(timestamp - event.getTimeStart()) < MAX_VALID_CALL_DURATION_MS)
|
||||
{
|
||||
event.update(timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
event = DMRDecodeEvent.builder(timestamp)
|
||||
.timeslot(getTimeslot())
|
||||
.identifiers(getMergedIdentifierCollection(identifiers))
|
||||
.eventDescription(DecodeEventType.CALL_DETECT.toString())
|
||||
.build();
|
||||
// mDetectedCallEventsMap.put(channel.getLogicalSlotNumber(), event);
|
||||
}
|
||||
}
|
||||
|
||||
//Only broadcast the call detect event if it doesn't match the current logical slot number
|
||||
// if(mCurrentLSN == null || mCurrentLSN != channel.getLogicalSlotNumber())
|
||||
// {
|
||||
// broadcast(event);
|
||||
// }
|
||||
return !isSameCall(event.getIdentifierCollection(), currentIdentifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1006,7 +1092,7 @@ public class DMRDecoderState extends TimeslotDecoderState
|
||||
ic.update(gpsInformation.getGPSLocation());
|
||||
|
||||
DecodeEvent gpsEvent = DMRDecodeEvent.builder(message.getTimestamp())
|
||||
.eventDescription(DecodeEventType.GPS.name())
|
||||
.eventDescription(DecodeEventType.GPS.toString())
|
||||
.identifiers(ic)
|
||||
.timeslot(message.getTimeslot())
|
||||
.details("LOCATION:" + gpsInformation.getGPSLocation())
|
||||
|
||||
@@ -313,7 +313,7 @@ public class DMRNetworkConfigurationMonitor
|
||||
|
||||
if(mTier3Model != null)
|
||||
{
|
||||
sb.append("\nNetwork Model").append(mTier3Model);
|
||||
sb.append("\nNetwork Model: ").append(mTier3Model);
|
||||
}
|
||||
|
||||
//Observed DMR Channels
|
||||
|
||||
@@ -41,6 +41,7 @@ import io.github.dsheirer.message.MessageHistoryResponse;
|
||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.event.DMRChannelGrantEvent;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.csbk.Opcode;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEvent;
|
||||
import io.github.dsheirer.module.decode.event.DecodeEventHistory;
|
||||
@@ -90,8 +91,12 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
IChannelEventProvider
|
||||
{
|
||||
private final static Logger mLog = LoggerFactory.getLogger(DMRTrafficChannelManager.class);
|
||||
public static final String CHANNEL_START_REJECTED = "CHANNEL START REJECTED";
|
||||
public static final String CHANNEL_START_REJECTED = " REJECTED - NO TUNER";
|
||||
public static final String DATA_CALL_IGNORED = "DATA CALL IGNORED";
|
||||
public static final String IGNORED = " - Ignored";
|
||||
public static final String MAX_TRAFFIC_CHANNELS_EXCEEDED = "MAX TRAFFIC CHANNELS EXCEEDED";
|
||||
public static final String NO_FREQUENCY = "NO FREQUENCY - CHECK LSN CHANNEL MAP";
|
||||
public static final long EVENT_TIME_STALE_THRESHOLD = 5000; //5 seconds
|
||||
|
||||
private Queue<Channel> mAvailableTrafficChannelQueue = new ConcurrentLinkedQueue<>();
|
||||
private List<Channel> mManagedTrafficChannels;
|
||||
@@ -325,7 +330,19 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn);
|
||||
|
||||
if(event != null && isSameCall(identifierCollection, event.getIdentifierCollection()))
|
||||
if(isStale(event, timestamp, identifierCollection)) //Create new event
|
||||
{
|
||||
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
.channel(channel)
|
||||
.eventDescription(getEventType(opcode, identifierCollection, encrypted).toString())
|
||||
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
|
||||
mLSNGrantEventMap.put(lsn, event);
|
||||
|
||||
}
|
||||
else //Update current event
|
||||
{
|
||||
Identifier from = getIdentifier(identifierCollection, Role.FROM);
|
||||
|
||||
@@ -336,115 +353,98 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
event.end(timestamp);
|
||||
|
||||
DMRChannelGrantEvent continuationGrantEvent = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
event = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
.channel(channel)
|
||||
.eventDescription(getEventType(opcode, event.isEncrypted()).toString() + " - Continue")
|
||||
.eventDescription(getEventType(opcode, identifierCollection, encrypted).toString() + " - Continue")
|
||||
.details("CHANNEL GRANT" + (encrypted ? " ENCRYPTED" : ""))
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
|
||||
mLSNGrantEventMap.put(lsn, continuationGrantEvent);
|
||||
broadcast(continuationGrantEvent);
|
||||
mLSNGrantEventMap.put(lsn, event);
|
||||
broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
//update the ending timestamp so that the duration value is correctly calculated
|
||||
event.update(timestamp);
|
||||
broadcast(event);
|
||||
|
||||
//Even though we have an event, the initial channel grant may have been rejected. Check to see if there
|
||||
//is a traffic channel allocated. If not, allocate one and update the event description.
|
||||
long frequency = channel.getDownlinkFrequency();
|
||||
|
||||
if(frequency > 0 && frequency != mCurrentControlFrequency &&
|
||||
!mAllocatedTrafficChannelFrequencyMap.containsKey(channel.getDownlinkFrequency()) &&
|
||||
!(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode()))
|
||||
{
|
||||
Channel trafficChannel = mAvailableTrafficChannelQueue.poll();
|
||||
|
||||
if(trafficChannel != null)
|
||||
{
|
||||
event.setEventDescription(getEventType(opcode, event.isEncrypted()).toString());
|
||||
event.setDetails("CHANNEL GRANT " + (encrypted ? " ENCRYPTED" : ""));
|
||||
event.setChannelDescriptor(channel);
|
||||
broadcast(event);
|
||||
|
||||
SourceConfigTuner sourceConfig = new SourceConfigTuner();
|
||||
sourceConfig.setFrequency(frequency);
|
||||
trafficChannel.setSourceConfiguration(sourceConfig);
|
||||
mAllocatedTrafficChannelFrequencyMap.put(frequency, trafficChannel);
|
||||
getInterModuleEventBus().post(new ChannelStartProcessingRequest(trafficChannel, channel, identifierCollection));
|
||||
}
|
||||
else
|
||||
{
|
||||
String description = (event.getEventDescription() != null ? event.getEventDescription() : "") +
|
||||
" - MAX TRAFFIC CHANNELS EXCEEDED";
|
||||
event.setEventDescription(description);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
|
||||
{
|
||||
DMRChannelGrantEvent channelGrantEvent = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
.encrypted(encrypted)
|
||||
.channel(channel)
|
||||
.eventDescription(getEventType(opcode, encrypted).toString() + " - Ignored")
|
||||
.details("DATA CALL IGNORED")
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
broadcast(event);
|
||||
|
||||
mLSNGrantEventMap.put(channel.getLogicalSlotNumber(), channelGrantEvent);
|
||||
broadcast(channelGrantEvent);
|
||||
return;
|
||||
}
|
||||
|
||||
DMRChannelGrantEvent channelGrantEvent = DMRChannelGrantEvent.channelGrantBuilder(timestamp)
|
||||
.encrypted(encrypted)
|
||||
.channel(channel)
|
||||
.eventDescription(getEventType(opcode, encrypted).toString())
|
||||
.details("CHANNEL GRANT")
|
||||
.identifiers(identifierCollection)
|
||||
.build();
|
||||
|
||||
mLSNGrantEventMap.put(channel.getLogicalSlotNumber(), channelGrantEvent);
|
||||
|
||||
//Allocate a traffic channel for the downlink frequency if one isn't already allocated
|
||||
//NOTE: we could also allocate a traffic channel for the uplink frequency here, in the future
|
||||
//Even though we have an event, the initial or continuation channel grant may have been rejected. Check to see
|
||||
// if there is a traffic channel allocated. If not, allocate one and update the event description.
|
||||
long frequency = channel.getDownlinkFrequency();
|
||||
|
||||
if(frequency > 0 && frequency != mCurrentControlFrequency &&
|
||||
!mAllocatedTrafficChannelFrequencyMap.containsKey(frequency))
|
||||
if(frequency == 0)
|
||||
{
|
||||
if(event.getDetails() == null)
|
||||
{
|
||||
event.setDetails(NO_FREQUENCY);
|
||||
}
|
||||
else if(!event.getDetails().endsWith(NO_FREQUENCY))
|
||||
{
|
||||
event.setDetails(event.getDetails() + " - " + NO_FREQUENCY);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(frequency != mCurrentControlFrequency && !mAllocatedTrafficChannelFrequencyMap.containsKey(frequency))
|
||||
{
|
||||
if(mIgnoreDataCalls && opcode.isDataChannelGrantOpcode())
|
||||
{
|
||||
if(event.getEventDescription() == null)
|
||||
{
|
||||
event.setEventDescription(getEventType(opcode, identifierCollection, encrypted) + IGNORED);
|
||||
}
|
||||
else if(!event.getEventDescription().endsWith(IGNORED))
|
||||
{
|
||||
event.setEventDescription(event.getEventDescription() + IGNORED);
|
||||
}
|
||||
|
||||
if(event.getDetails() == null)
|
||||
{
|
||||
event.setDetails(DATA_CALL_IGNORED);
|
||||
}
|
||||
else if(!event.getDetails().endsWith(DATA_CALL_IGNORED))
|
||||
{
|
||||
event.setDetails(event.getDetails() + " - " + DATA_CALL_IGNORED);
|
||||
}
|
||||
|
||||
broadcast(event);
|
||||
return;
|
||||
}
|
||||
|
||||
Channel trafficChannel = mAvailableTrafficChannelQueue.poll();
|
||||
|
||||
if(trafficChannel == null)
|
||||
{
|
||||
String details = (channelGrantEvent.getDetails() != null ? channelGrantEvent.getDetails() : "") +
|
||||
" " + MAX_TRAFFIC_CHANNELS_EXCEEDED;
|
||||
channelGrantEvent.setDetails(details);
|
||||
channelGrantEvent.setEventDescription(channelGrantEvent.getEventDescription() + " - Ignored");
|
||||
}
|
||||
else
|
||||
if(trafficChannel != null)
|
||||
{
|
||||
SourceConfigTuner sourceConfig = new SourceConfigTuner();
|
||||
sourceConfig.setFrequency(frequency);
|
||||
trafficChannel.setSourceConfiguration(sourceConfig);
|
||||
mAllocatedTrafficChannelFrequencyMap.put(frequency, trafficChannel);
|
||||
broadcast(ChannelEvent.requestEnable(trafficChannel));
|
||||
getInterModuleEventBus().post(new ChannelStartProcessingRequest(trafficChannel, channel,
|
||||
identifierCollection));
|
||||
}
|
||||
else
|
||||
{
|
||||
if(event.getDetails() == null)
|
||||
{
|
||||
event.setDetails(MAX_TRAFFIC_CHANNELS_EXCEEDED);
|
||||
}
|
||||
else if(!event.getDetails().endsWith(MAX_TRAFFIC_CHANNELS_EXCEEDED))
|
||||
{
|
||||
event.setDetails(event.getDetails() + " - " + MAX_TRAFFIC_CHANNELS_EXCEEDED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
broadcast(channelGrantEvent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a call event type description for the specified opcode and service options
|
||||
*/
|
||||
private DecodeEventType getEventType(Opcode opcode, boolean encrypted)
|
||||
private DecodeEventType getEventType(Opcode opcode, IdentifierCollection identifierCollection, boolean encrypted)
|
||||
{
|
||||
DecodeEventType type = null;
|
||||
|
||||
@@ -452,10 +452,22 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
case STANDARD_TALKGROUP_VOICE_CHANNEL_GRANT:
|
||||
case STANDARD_BROADCAST_TALKGROUP_VOICE_CHANNEL_GRANT:
|
||||
case MOTOROLA_CONPLUS_VOICE_CHANNEL_USER:
|
||||
type = encrypted ? DecodeEventType.CALL_GROUP_ENCRYPTED : DecodeEventType.CALL_GROUP;
|
||||
break;
|
||||
|
||||
case MOTOROLA_CONPLUS_VOICE_CHANNEL_USER:
|
||||
Identifier to = identifierCollection.getToIdentifier();
|
||||
|
||||
if(to instanceof DMRRadio)
|
||||
{
|
||||
type = encrypted ? DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED : DecodeEventType.CALL_UNIT_TO_UNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
type = encrypted ? DecodeEventType.CALL_GROUP_ENCRYPTED : DecodeEventType.CALL_GROUP;
|
||||
}
|
||||
break;
|
||||
|
||||
case STANDARD_PRIVATE_VOICE_CHANNEL_GRANT:
|
||||
case STANDARD_DUPLEX_PRIVATE_VOICE_CHANNEL_GRANT:
|
||||
type = encrypted ? DecodeEventType.CALL_UNIT_TO_UNIT_ENCRYPTED : DecodeEventType.CALL_UNIT_TO_UNIT;
|
||||
@@ -535,6 +547,31 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
return Objects.equals(toIdentifier1, toIdentifier2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if the event is a stale event, meaning that the event is null, or the event start exceeds the max
|
||||
* valid call duration threshold, or if the event identifiers don't match the current identifiers.
|
||||
*
|
||||
* @param event to check for staleness
|
||||
* @param timestamp to check the event against
|
||||
* @param currentIdentifiers to compare against the event
|
||||
* @return true if the event is stale.
|
||||
*/
|
||||
private boolean isStale(DecodeEvent event, long timestamp, IdentifierCollection currentIdentifiers)
|
||||
{
|
||||
if(event == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if(timestamp - event.getTimeEnd() > EVENT_TIME_STALE_THRESHOLD)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isSameCall(event.getIdentifierCollection(), currentIdentifiers);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the first identifier with a TO role.
|
||||
*
|
||||
@@ -617,9 +654,8 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
/**
|
||||
* Removes any call events that are associated with the specified frequency.
|
||||
* @param frequency that was removed
|
||||
* @param description to optionally append to the details for the event
|
||||
*/
|
||||
private void cleanupCallEvents(long frequency, String description)
|
||||
private void removeCallEvents(long frequency)
|
||||
{
|
||||
List<Integer> lsnsToRemove = new ArrayList<>();
|
||||
|
||||
@@ -633,20 +669,35 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
|
||||
for(Integer lsn: lsnsToRemove)
|
||||
{
|
||||
DMRChannelGrantEvent event = mLSNGrantEventMap.remove(lsn);
|
||||
mLSNGrantEventMap.remove(lsn);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateCallEventDetails(long frequency, String detailFragment)
|
||||
{
|
||||
List<Integer> lsnsToUpdate = new ArrayList<>();
|
||||
|
||||
for(Map.Entry<Integer,DMRChannelGrantEvent> entry: mLSNGrantEventMap.entrySet())
|
||||
{
|
||||
if(entry.getValue().getChannelDescriptor().getDownlinkFrequency() == frequency)
|
||||
{
|
||||
lsnsToUpdate.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
for(Integer lsn: lsnsToUpdate)
|
||||
{
|
||||
DMRChannelGrantEvent event = mLSNGrantEventMap.get(lsn);
|
||||
|
||||
if(event != null)
|
||||
{
|
||||
if(description != null)
|
||||
if(event.getDetails() == null)
|
||||
{
|
||||
if(event.getEventDescription() != null)
|
||||
{
|
||||
event.setEventDescription(event.getEventDescription() + " - " + description);
|
||||
}
|
||||
else
|
||||
{
|
||||
event.setEventDescription(description);
|
||||
}
|
||||
event.setDetails(detailFragment);
|
||||
}
|
||||
else if(!event.getDetails().endsWith(detailFragment))
|
||||
{
|
||||
event.setDetails(event.getDetails() + detailFragment);
|
||||
}
|
||||
|
||||
broadcast(event);
|
||||
@@ -659,36 +710,64 @@ public class DMRTrafficChannelManager extends TrafficChannelManager implements I
|
||||
{
|
||||
Channel channel = channelEvent.getChannel();
|
||||
|
||||
if(channel.isTrafficChannel() && (channelEvent.getEvent() == Event.NOTIFICATION_PROCESSING_STOP ||
|
||||
channelEvent.getEvent() == Event.NOTIFICATION_PROCESSING_START_REJECTED))
|
||||
if(channel.isTrafficChannel())
|
||||
{
|
||||
String optionalDescription = (channelEvent.getEvent() == Event.NOTIFICATION_PROCESSING_START_REJECTED ?
|
||||
"Rejected" : null);
|
||||
|
||||
long frequencyToRemove = 0;
|
||||
|
||||
for(Map.Entry<Long,Channel> entry: mAllocatedTrafficChannelFrequencyMap.entrySet())
|
||||
switch(channelEvent.getEvent())
|
||||
{
|
||||
if(channel == entry.getValue() && entry.getKey() != null)
|
||||
{
|
||||
frequencyToRemove = entry.getKey();
|
||||
cleanupCallEvents(entry.getKey(), optionalDescription);
|
||||
case NOTIFICATION_PROCESSING_STOP:
|
||||
long frequencyToRemove = 0;
|
||||
|
||||
for(Map.Entry<Long,Channel> entry: mAllocatedTrafficChannelFrequencyMap.entrySet())
|
||||
{
|
||||
if(channel == entry.getValue() && entry.getKey() != null)
|
||||
{
|
||||
frequencyToRemove = entry.getKey();
|
||||
removeCallEvents(entry.getKey());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(frequencyToRemove > 0)
|
||||
{
|
||||
mAllocatedTrafficChannelFrequencyMap.remove(frequencyToRemove);
|
||||
|
||||
//Unlock the frequency in the channel rotation monitor
|
||||
getInterModuleEventBus().post(FrequencyLockChangeRequest.unlock(frequencyToRemove));
|
||||
}
|
||||
|
||||
//Add the traffic channel back to the queue to be reused
|
||||
if(!mAvailableTrafficChannelQueue.contains(channel))
|
||||
{
|
||||
mAvailableTrafficChannelQueue.add(channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
case NOTIFICATION_PROCESSING_START_REJECTED:
|
||||
long frequencyToUpdate = 0;
|
||||
|
||||
if(frequencyToRemove > 0)
|
||||
{
|
||||
mAllocatedTrafficChannelFrequencyMap.remove(frequencyToRemove);
|
||||
for(Map.Entry<Long,Channel> entry: mAllocatedTrafficChannelFrequencyMap.entrySet())
|
||||
{
|
||||
if(channel == entry.getValue() && entry.getKey() != null)
|
||||
{
|
||||
frequencyToUpdate = entry.getKey();
|
||||
updateCallEventDetails(entry.getKey(), CHANNEL_START_REJECTED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Unlock the frequency in the channel rotation monitor
|
||||
getInterModuleEventBus().post(FrequencyLockChangeRequest.unlock(frequencyToRemove));
|
||||
}
|
||||
if(frequencyToUpdate > 0)
|
||||
{
|
||||
mAllocatedTrafficChannelFrequencyMap.remove(frequencyToUpdate);
|
||||
|
||||
//Add the traffic channel back to the queue to be reused
|
||||
if(!mAvailableTrafficChannelQueue.contains(channel))
|
||||
{
|
||||
mAvailableTrafficChannelQueue.add(channel);
|
||||
//Unlock the frequency in the channel rotation monitor
|
||||
getInterModuleEventBus().post(FrequencyLockChangeRequest.unlock(frequencyToUpdate));
|
||||
}
|
||||
|
||||
//Add the traffic channel back to the queue to be reused
|
||||
if(!mAvailableTrafficChannelQueue.contains(channel))
|
||||
{
|
||||
mAvailableTrafficChannelQueue.add(channel);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,8 +34,12 @@ import java.util.List;
|
||||
*/
|
||||
public class DecodeConfigDMR extends DecodeConfiguration
|
||||
{
|
||||
public static final int CHANNEL_ROTATION_DELAY_MINIMUM_MS = 200;
|
||||
public static final int CHANNEL_ROTATION_DELAY_DEFAULT_MS = 500;
|
||||
public static final int CHANNEL_ROTATION_DELAY_MAXIMUM_MS = 2000;
|
||||
private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT;
|
||||
private boolean mIgnoreDataCalls = true;
|
||||
private boolean mIgnoreCRCChecksums = false;
|
||||
private List<TimeslotFrequency> mTimeslotMap = new ArrayList<>();
|
||||
|
||||
public DecodeConfigDMR()
|
||||
@@ -87,6 +91,24 @@ public class DecodeConfigDMR extends DecodeConfiguration
|
||||
mIgnoreDataCalls = ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if decoder should ignore CRC checksums when validating decoded messages
|
||||
*/
|
||||
@JacksonXmlProperty(isAttribute = true, localName = "ignore_crc")
|
||||
public boolean getIgnoreCRCChecksums()
|
||||
{
|
||||
return mIgnoreCRCChecksums;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets flag to ignore CRC checksums
|
||||
* @param ignore true to ignore CRC checksums.
|
||||
*/
|
||||
public void setIgnoreCRCChecksums(boolean ignore)
|
||||
{
|
||||
mIgnoreCRCChecksums = ignore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traffic channel pool size.
|
||||
* @return
|
||||
|
||||
@@ -88,7 +88,6 @@ public class DMRLogicalChannel extends DMRChannel
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("LSN:").append(getLogicalSlotNumber());
|
||||
sb.append(" LCN:").append(getValue());
|
||||
sb.append(" TS:").append(getTimeslot());
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.github.dsheirer.module.decode.dmr.channel;
|
||||
|
||||
/**
|
||||
* DMR Tier III Trunking Channel
|
||||
*/
|
||||
public class DMRTier3Channel extends DMRLogicalChannel
|
||||
{
|
||||
/**
|
||||
* Constructs an instance. Note: radio reference uses a one based index, so we add a value of one to the
|
||||
* calculated logical slot value for visual compatibility for users.
|
||||
*
|
||||
* @param channel number or repeater number
|
||||
* @param timeslot
|
||||
*/
|
||||
public DMRTier3Channel(int channel, int timeslot)
|
||||
{
|
||||
super(channel, timeslot);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logical slot number for this channel.
|
||||
*
|
||||
* Formula: LSN = (channel * 2) + timeslot
|
||||
*
|
||||
* @return logical slot number, a 1-based index value
|
||||
*/
|
||||
@Override
|
||||
public int getLogicalSlotNumber()
|
||||
{
|
||||
int repeater = getRepeater();
|
||||
|
||||
if(repeater > 0)
|
||||
{
|
||||
return ((repeater) * 2) + getTimeslot();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -79,8 +79,6 @@ public enum Opcode
|
||||
MOTOROLA_CONPLUS_DATA_WINDOW_ANNOUNCEMENT(Vendor.MOTOROLA_CONNECT_PLUS, 28, "ENHANCED DATA REVERT WINDOW ANNOUNCEMENT"),
|
||||
MOTOROLA_CONPLUS_DATA_WINDOW_GRANT(Vendor.MOTOROLA_CONNECT_PLUS, 29, "ENHANCED DATA REVERT WINDOW GRANT"),
|
||||
|
||||
//TODO: Capacity Max (Tier III) also supports Enhanced Data Revert channels with windowing ... they either
|
||||
//TODO: use CSBKO 41 and 42, or they have a different pair of messages.
|
||||
MOTOROLA_CAPMAX_ALOHA(Vendor.MOTOROLA_CAPACITY_PLUS, 25, "ALOHA"),
|
||||
|
||||
MOTOROLA_CAPPLUS_CALL_ALERT(Vendor.MOTOROLA_CAPACITY_PLUS, 31, "CALL ALERT"),
|
||||
|
||||
@@ -26,6 +26,7 @@ import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
@@ -208,7 +209,7 @@ public class Clear extends CSBKMessage implements ITimeslotFrequencyReceiver
|
||||
|
||||
if(mChannel == null)
|
||||
{
|
||||
mChannel = new DMRLogicalChannel(getMoveToChannelNumber(), getMoveToTimeslot());
|
||||
mChannel = new DMRTier3Channel(getMoveToChannelNumber(), getMoveToTimeslot());
|
||||
}
|
||||
|
||||
return mChannel;
|
||||
|
||||
@@ -24,7 +24,7 @@ import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.identifier.radio.RadioIdentifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
|
||||
import io.github.dsheirer.module.decode.dmr.identifier.DMRRadio;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
@@ -156,7 +156,7 @@ public class MoveTSCC extends CSBKMessage
|
||||
}
|
||||
else
|
||||
{
|
||||
mDMRChannel = new DMRLogicalChannel(getMessage().getInt(CHANNEL_NUMBER), 1);
|
||||
mDMRChannel = new DMRTier3Channel(getMessage().getInt(CHANNEL_NUMBER), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
@@ -121,7 +122,7 @@ public class AdjacentSiteInformation extends Announcement implements ITimeslotFr
|
||||
if(mChannel == null)
|
||||
{
|
||||
//Timeslot hard-coded to 1
|
||||
mChannel = new DMRLogicalChannel(getMessage().getInt(NEIGHBOR_CHANNEL_NUMBER), 1);
|
||||
mChannel = new DMRTier3Channel(getMessage().getInt(NEIGHBOR_CHANNEL_NUMBER), 1);
|
||||
}
|
||||
|
||||
return mChannel;
|
||||
|
||||
@@ -23,7 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.SlotType;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.mbc.MBCContinuationBlock;
|
||||
@@ -221,7 +221,7 @@ public class AnnounceWithdrawTSCC extends Announcement
|
||||
}
|
||||
else
|
||||
{
|
||||
mChannel1 = new DMRLogicalChannel(getMessage().getInt(CHANNEL_NUMBER_1), 1);
|
||||
mChannel1 = new DMRTier3Channel(getMessage().getInt(CHANNEL_NUMBER_1), 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +235,7 @@ public class AnnounceWithdrawTSCC extends Announcement
|
||||
{
|
||||
if(mChannel2 == null)
|
||||
{
|
||||
mChannel2 = new DMRLogicalChannel(getMessage().getInt(CHANNEL_NUMBER_2), 1);
|
||||
mChannel2 = new DMRTier3Channel(getMessage().getInt(CHANNEL_NUMBER_2), 1);
|
||||
}
|
||||
|
||||
return mChannel2;
|
||||
|
||||
@@ -24,6 +24,7 @@ import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
@@ -203,7 +204,7 @@ public class VoteNowAdvice extends Announcement implements ITimeslotFrequencyRec
|
||||
|
||||
if(mChannel == null)
|
||||
{
|
||||
mChannel = new DMRLogicalChannel(getMessage().getInt(VOTED_CHANNEL_NUMBER), 1);
|
||||
mChannel = new DMRTier3Channel(getMessage().getInt(VOTED_CHANNEL_NUMBER), 1);
|
||||
}
|
||||
|
||||
return mChannel;
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.DMRSyncPattern;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRLogicalChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.DMRTier3Channel;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.ITimeslotFrequencyReceiver;
|
||||
import io.github.dsheirer.module.decode.dmr.channel.TimeslotFrequency;
|
||||
import io.github.dsheirer.module.decode.dmr.message.CACH;
|
||||
@@ -148,7 +149,7 @@ public abstract class ChannelGrant extends CSBKMessage implements ITimeslotFrequ
|
||||
|
||||
if(mChannel == null)
|
||||
{
|
||||
mChannel = new DMRLogicalChannel(getChannelNumber(), getChannelGrantTimeslot());
|
||||
mChannel = new DMRTier3Channel(getChannelNumber(), getChannelGrantTimeslot());
|
||||
}
|
||||
|
||||
return mChannel;
|
||||
|
||||
@@ -41,6 +41,7 @@ import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ControlChanne
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.HyteraXPTChannel;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.NullMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.ShortLCMessage;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.TrafficChannelSystemParameters;
|
||||
import io.github.dsheirer.module.decode.dmr.message.data.lc.shorty.UnknownShortLCMessage;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -152,11 +153,13 @@ public class LCMessageFactory
|
||||
case SHORT_STANDARD_CONTROL_CHANNEL_SYSTEM_PARAMETERS:
|
||||
slc = new ControlChannelSystemParameters(message, timestamp, timeslot);
|
||||
break;
|
||||
case SHORT_STANDARD_TRAFFIC_CHANNEL_SYSTEM_PARAMETERS:
|
||||
slc = new TrafficChannelSystemParameters(message, timestamp, timeslot);
|
||||
break;
|
||||
case SHORT_HYTERA_XPT_CHANNEL:
|
||||
case SHORT_STANDARD_XPT_CHANNEL:
|
||||
slc = new HyteraXPTChannel(message, timestamp, timeslot);
|
||||
break;
|
||||
case SHORT_STANDARD_TRAFFIC_CHANNEL_SYSTEM_PARAMETERS:
|
||||
case SHORT_STANDARD_UNKNOWN:
|
||||
default:
|
||||
slc = new UnknownShortLCMessage(message, timestamp, timeslot);
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* *****************************************************************************
|
||||
* Copyright (C) 2014-2020 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.module.decode.dmr.message.data.lc.shorty;
|
||||
|
||||
import io.github.dsheirer.bits.CorrectedBinaryMessage;
|
||||
import io.github.dsheirer.identifier.Identifier;
|
||||
import io.github.dsheirer.module.decode.dmr.message.type.SystemIdentityCode;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* DMR Tier III - Traffic Channel System Parameters
|
||||
*/
|
||||
public class TrafficChannelSystemParameters extends ShortLCMessage
|
||||
{
|
||||
private static final int SYSTEM_IDENTITY_CODE_OFFSET = 4;
|
||||
private static final int REGISTRATION_REQUIRED_FLAG = 18;
|
||||
private static final int[] COMMON_SLOT_COUNTER = new int[]{19, 20, 21, 22, 23, 24, 25, 26, 27};
|
||||
|
||||
private SystemIdentityCode mSystemIdentityCode;
|
||||
private List<Identifier> mIdentifiers;
|
||||
|
||||
/**
|
||||
* Constructs an instance
|
||||
*
|
||||
* @param message containing link control data
|
||||
*/
|
||||
public TrafficChannelSystemParameters(CorrectedBinaryMessage message, long timestamp, int timeslot)
|
||||
{
|
||||
super(message, timestamp, timeslot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if(!isValid())
|
||||
{
|
||||
sb.append("[CRC ERROR] ");
|
||||
}
|
||||
sb.append("SLC TIER III TRAFFIC CHANNEL ").append(getSystemIdentityCode().getModel());
|
||||
sb.append(" NET:").append(getSystemIdentityCode().getNetwork());
|
||||
sb.append(" SITE:").append(getSystemIdentityCode().getSite());
|
||||
if(isRegistrationRequired())
|
||||
{
|
||||
sb.append(" REGISTRATION REQUIRED");
|
||||
}
|
||||
sb.append(" SLOT COUNTER:").append(getCommonSlotCounter());
|
||||
sb.append(" MSG:").append(getMessage().toHexString());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* System Identity Code structure
|
||||
*/
|
||||
public SystemIdentityCode getSystemIdentityCode()
|
||||
{
|
||||
if(mSystemIdentityCode == null)
|
||||
{
|
||||
mSystemIdentityCode = new SystemIdentityCode(getMessage(), SYSTEM_IDENTITY_CODE_OFFSET, false);
|
||||
}
|
||||
|
||||
return mSystemIdentityCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates if registration is required to access this network
|
||||
*/
|
||||
public boolean isRegistrationRequired()
|
||||
{
|
||||
return getMessage().get(REGISTRATION_REQUIRED_FLAG);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common Slot Counter
|
||||
*/
|
||||
public int getCommonSlotCounter()
|
||||
{
|
||||
return getMessage().getInt(COMMON_SLOT_COUNTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Identifier> getIdentifiers()
|
||||
{
|
||||
if(mIdentifiers == null)
|
||||
{
|
||||
mIdentifiers = new ArrayList<>();
|
||||
mIdentifiers.add(getSystemIdentityCode().getNetwork());
|
||||
mIdentifiers.add(getSystemIdentityCode().getSite());
|
||||
}
|
||||
|
||||
return mIdentifiers;
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import io.github.dsheirer.protocol.Protocol;
|
||||
public class DecodeEvent implements IDecodeEvent
|
||||
{
|
||||
private long mTimeStart;
|
||||
private long mDuration;
|
||||
private long mTimeEnd;
|
||||
private String mEventDescription;
|
||||
private IdentifierCollection mIdentifierCollection;
|
||||
private IChannelDescriptor mChannelDescriptor;
|
||||
@@ -66,7 +66,7 @@ public class DecodeEvent implements IDecodeEvent
|
||||
{
|
||||
if(timestamp > mTimeStart)
|
||||
{
|
||||
mDuration = timestamp - mTimeStart;
|
||||
mTimeEnd = timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,13 +87,26 @@ public class DecodeEvent implements IDecodeEvent
|
||||
return mTimeStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event end in milliseconds
|
||||
*/
|
||||
public long getTimeEnd()
|
||||
{
|
||||
return mTimeEnd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event duration in milliseconds
|
||||
*/
|
||||
@Override
|
||||
public long getDuration()
|
||||
{
|
||||
return mDuration;
|
||||
if(mTimeEnd < mTimeStart)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mTimeEnd - mTimeStart;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,7 +114,7 @@ public class DecodeEvent implements IDecodeEvent
|
||||
*/
|
||||
public void setDuration(long duration)
|
||||
{
|
||||
mDuration = duration;
|
||||
mTimeEnd = mTimeStart + duration;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,7 +242,7 @@ public class DecodeEvent implements IDecodeEvent
|
||||
{
|
||||
sb.append(" IDS:").append(Joiner.on(",").join(mIdentifierCollection.getIdentifiers()));
|
||||
}
|
||||
sb.append(" DURATION:").append(mDuration);
|
||||
sb.append(" DURATION:").append(getDuration());
|
||||
sb.append(" CHANNEL:").append(mChannelDescriptor);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,12 @@ public interface IDecodeEvent
|
||||
*/
|
||||
long getTimeStart();
|
||||
|
||||
/**
|
||||
* Event end time
|
||||
* @return timestamp in milliseconds
|
||||
*/
|
||||
long getTimeEnd();
|
||||
|
||||
/**
|
||||
* Event duration.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,10 @@ import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigMPT1327 extends DecodeConfiguration implements WithCallTimeout
|
||||
{
|
||||
public static final int CHANNEL_ROTATION_DELAY_MINIMUM_MS = 500;
|
||||
public static final int CHANNEL_ROTATION_DELAY_DEFAULT_MS = 500;
|
||||
public static final int CHANNEL_ROTATION_DELAY_MAXIMUM_MS = 2000;
|
||||
|
||||
private String mChannelMapName;
|
||||
private Sync mSync = Sync.NORMAL;
|
||||
|
||||
|
||||
@@ -30,6 +30,10 @@ import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||
|
||||
public class DecodeConfigP25Phase1 extends DecodeConfiguration
|
||||
{
|
||||
public static final int CHANNEL_ROTATION_DELAY_MINIMUM_MS = 400;
|
||||
public static final int CHANNEL_ROTATION_DELAY_DEFAULT_MS = 500;
|
||||
public static final int CHANNEL_ROTATION_DELAY_MAXIMUM_MS = 2000;
|
||||
|
||||
private P25P1Decoder.Modulation mModulation = P25P1Decoder.Modulation.C4FM;
|
||||
|
||||
private int mTrafficChannelPoolSize = TRAFFIC_CHANNEL_LIMIT_DEFAULT;
|
||||
|
||||
@@ -823,15 +823,20 @@ public class P25P1DecoderState extends DecoderState implements IChannelEventList
|
||||
}
|
||||
else
|
||||
{
|
||||
mCurrentCallEvent.setIdentifierCollection(getIdentifierCollection().copyOf());
|
||||
mCurrentCallEvent.end(timestamp);
|
||||
broadcast(mCurrentCallEvent);
|
||||
|
||||
if(type == DecodeEventType.CALL_ENCRYPTED)
|
||||
{
|
||||
mCurrentCallEvent.setEventDescription(type.toString());
|
||||
mCurrentCallEvent.setDetails(details);
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.ENCRYPTED));
|
||||
}
|
||||
else
|
||||
{
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL));
|
||||
}
|
||||
mCurrentCallEvent.setIdentifierCollection(getIdentifierCollection().copyOf());
|
||||
mCurrentCallEvent.end(timestamp);
|
||||
broadcast(mCurrentCallEvent);
|
||||
broadcast(new DecoderStateEvent(this, Event.CONTINUATION, State.CALL));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ public class AdjacentStatusBroadcast extends OSPMessage implements IFrequencyBan
|
||||
sb.append(" SYSTEM:").append(getSystem());
|
||||
sb.append(" RFSS:").append(getRfss());
|
||||
sb.append(" SITE:").append(getSite());
|
||||
sb.append(" CHANNEL:").append(getChannel());
|
||||
sb.append(" LRA:").append(getLocationRegistrationArea());
|
||||
sb.append(" FLAGS ").append(getSiteFlags());
|
||||
sb.append(" SERVICES ").append(getSystemServiceClass());
|
||||
|
||||
@@ -19,6 +19,8 @@ import io.github.dsheirer.source.SourceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
/**
|
||||
* Monitors measured frequency error PPM values from the tuner and (when enabled) applies the current
|
||||
* error measurement to the tuner controller to adjust the tuner's PPM and align the frequency value.
|
||||
@@ -32,6 +34,7 @@ public class FrequencyErrorCorrectionManager
|
||||
private long mObservationPeriodStart;
|
||||
private double mPPMRequired;
|
||||
private boolean mEnabled = true;
|
||||
private DecimalFormat mDecimalFormat = new DecimalFormat("0.0");
|
||||
private TunerController mTunerController;
|
||||
|
||||
/**
|
||||
@@ -72,7 +75,7 @@ public class FrequencyErrorCorrectionManager
|
||||
|
||||
try
|
||||
{
|
||||
mLog.info("Auto-Correcting Tuner PPM to [" + frequencyCorrection + "]");
|
||||
mLog.info("Auto-Correcting Tuner PPM to [" + mDecimalFormat.format(frequencyCorrection) + "]");
|
||||
|
||||
mTunerController.setFrequencyCorrection(frequencyCorrection);
|
||||
reset();
|
||||
|
||||
@@ -275,6 +275,14 @@ public abstract class TunerController implements Tunable, ISourceEventProcessor,
|
||||
return (int)(getBandwidth() * mUsableBandwidthPercentage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the usable bandwidth percentage -- this can change based on samplerate for some tuners
|
||||
*/
|
||||
public void setUsableBandwidthPercentage(double usableBandwidthPercentage)
|
||||
{
|
||||
mUsableBandwidthPercentage = usableBandwidthPercentage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usable half bandwidth - total bandwidth minus unusable space at either end of the spectrum.
|
||||
*
|
||||
|
||||
@@ -41,6 +41,7 @@ import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class AirspyTunerController extends USBTunerController
|
||||
{
|
||||
@@ -79,6 +80,7 @@ public class AirspyTunerController extends USBTunerController
|
||||
private AirspyDeviceInformation mDeviceInfo;
|
||||
private List<AirspySampleRate> mSampleRates = new ArrayList<>();
|
||||
private int mSampleRate = 0;
|
||||
private ReentrantLock mLock = new ReentrantLock();
|
||||
private USBTransferProcessor mUSBTransferProcessor;
|
||||
|
||||
public AirspyTunerController(Device device) throws SourceException
|
||||
@@ -347,32 +349,35 @@ public class AirspyTunerController extends USBTunerController
|
||||
@Override
|
||||
public void setTunedFrequency(long frequency) throws SourceException
|
||||
{
|
||||
if(FREQUENCY_MIN <= frequency && frequency <= FREQUENCY_MAX)
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(4);
|
||||
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
buffer.putInt((int) frequency);
|
||||
|
||||
buffer.rewind();
|
||||
|
||||
try
|
||||
if(FREQUENCY_MIN <= frequency && frequency <= FREQUENCY_MAX)
|
||||
{
|
||||
write(Command.SET_FREQUENCY, 0, 0, buffer);
|
||||
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);
|
||||
}
|
||||
}
|
||||
catch(UsbException e)
|
||||
else
|
||||
{
|
||||
mLog.error("error setting frequency [" + frequency + "]", e);
|
||||
|
||||
throw new SourceException("error setting frequency [" +
|
||||
frequency + "]", e);
|
||||
throw new SourceException("Frequency [" + frequency + "] outside " + "of tunable range " + FREQUENCY_MIN + "-" + FREQUENCY_MAX);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
throw new SourceException("Frequency [" + frequency + "] outside "
|
||||
+ "of tunable range " + FREQUENCY_MIN + "-" + FREQUENCY_MAX);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -406,6 +411,25 @@ public class AirspyTunerController extends USBTunerController
|
||||
{
|
||||
mSampleRate = rate.getRate();
|
||||
mFrequencyController.setSampleRate(mSampleRate);
|
||||
|
||||
// Update the Usable Bandwidth Percentage, as the usable range varies with the sample rate.
|
||||
// Values below are from https://github.com/DSheirer/sdrtrunk/issues/815#issuecomment-699859590
|
||||
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.60);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -475,14 +499,22 @@ public class AirspyTunerController extends USBTunerController
|
||||
* @throws LibUsbException on unsuccessful read operation
|
||||
* @throws UsbException on USB error
|
||||
*/
|
||||
public void setMixerAGC(boolean enabled)
|
||||
throws LibUsbException, UsbException
|
||||
public void setMixerAGC(boolean enabled) throws LibUsbException, UsbException
|
||||
{
|
||||
int result = readByte(Command.SET_MIXER_AGC, 0, (enabled ? 1 : 0), true);
|
||||
mLock.lock();
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
try
|
||||
{
|
||||
throw new UsbException("Couldnt set mixer AGC enabled: " + enabled);
|
||||
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);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -495,11 +527,20 @@ public class AirspyTunerController extends USBTunerController
|
||||
*/
|
||||
public void setLNAAGC(boolean enabled) throws LibUsbException, UsbException
|
||||
{
|
||||
int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true);
|
||||
mLock.lock();
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
try
|
||||
{
|
||||
throw new UsbException("Couldnt set LNA AGC enabled: " + enabled);
|
||||
int result = readByte(Command.SET_LNA_AGC, 0, (enabled ? 1 : 0), true);
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
{
|
||||
throw new UsbException("Couldnt set LNA AGC enabled: " + enabled);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,22 +564,30 @@ public class AirspyTunerController extends USBTunerController
|
||||
* @throws UsbException on error in USB transfer
|
||||
* @throws IllegalArgumentException if gain value is invalid
|
||||
*/
|
||||
public void setLNAGain(int gain)
|
||||
throws LibUsbException, UsbException, IllegalArgumentException
|
||||
public void setLNAGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException
|
||||
{
|
||||
if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX)
|
||||
{
|
||||
int result = readByte(Command.SET_LNA_GAIN, 0, gain, true);
|
||||
mLock.lock();
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
try
|
||||
{
|
||||
if(LNA_GAIN_MIN <= gain && gain <= LNA_GAIN_MAX)
|
||||
{
|
||||
throw new UsbException("Couldnt set LNA gain to: " + gain);
|
||||
int result = readByte(Command.SET_LNA_GAIN, 0, gain, true);
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
{
|
||||
throw new UsbException("Couldnt set LNA gain to: " + gain);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("LNA gain value [" + gain +
|
||||
"] is outside value range: " + LNA_GAIN_MIN + "-" + LNA_GAIN_MAX);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
throw new IllegalArgumentException("LNA gain value [" + gain +
|
||||
"] is outside value range: " + LNA_GAIN_MIN + "-" + LNA_GAIN_MAX);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -550,22 +599,30 @@ public class AirspyTunerController extends USBTunerController
|
||||
* @throws UsbException on error in USB transfer
|
||||
* @throws IllegalArgumentException if gain value is invalid
|
||||
*/
|
||||
public void setMixerGain(int gain)
|
||||
throws LibUsbException, UsbException, IllegalArgumentException
|
||||
public void setMixerGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException
|
||||
{
|
||||
if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX)
|
||||
{
|
||||
int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true);
|
||||
mLock.lock();
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
try
|
||||
{
|
||||
if(MIXER_GAIN_MIN <= gain && gain <= MIXER_GAIN_MAX)
|
||||
{
|
||||
throw new UsbException("Couldnt set mixer gain to: " + gain);
|
||||
int result = readByte(Command.SET_MIXER_GAIN, 0, gain, true);
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
{
|
||||
throw new UsbException("Couldnt set mixer gain to: " + gain);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Mixer gain value [" + gain +
|
||||
"] is outside value range: " + MIXER_GAIN_MIN + "-" + MIXER_GAIN_MAX);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
throw new IllegalArgumentException("Mixer gain value [" + gain +
|
||||
"] is outside value range: " + MIXER_GAIN_MIN + "-" + MIXER_GAIN_MAX);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -577,22 +634,30 @@ public class AirspyTunerController extends USBTunerController
|
||||
* @throws UsbException on error in USB transfer
|
||||
* @throws IllegalArgumentException if gain value is invalid
|
||||
*/
|
||||
public void setIFGain(int gain)
|
||||
throws LibUsbException, UsbException, IllegalArgumentException
|
||||
public void setIFGain(int gain) throws LibUsbException, UsbException, IllegalArgumentException
|
||||
{
|
||||
if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX)
|
||||
{
|
||||
int result = readByte(Command.SET_VGA_GAIN, 0, gain, true);
|
||||
mLock.lock();
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
try
|
||||
{
|
||||
if(IF_GAIN_MIN <= gain && gain <= IF_GAIN_MAX)
|
||||
{
|
||||
throw new UsbException("Couldnt set VGA gain to: " + gain);
|
||||
int result = readByte(Command.SET_VGA_GAIN, 0, gain, true);
|
||||
|
||||
if(result != LibUsb.SUCCESS)
|
||||
{
|
||||
throw new UsbException("Couldnt set VGA gain to: " + gain);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("VGA gain value [" + gain +
|
||||
"] is outside value range: " + IF_GAIN_MIN + "-" + IF_GAIN_MAX);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
throw new IllegalArgumentException("VGA gain value [" + gain +
|
||||
"] is outside value range: " + IF_GAIN_MIN + "-" + IF_GAIN_MAX);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ public class ChannelRotationMonitor extends Module implements ISourceEventProvid
|
||||
Listener<DecoderStateEvent>
|
||||
{
|
||||
public static final int CHANNEL_ROTATION_DELAY_MINIMUM = 200;
|
||||
public static final int CHANNEL_ROTATION_DELAY_DEFAULT = 500;
|
||||
public static final int CHANNEL_ROTATION_DELAY_MAXIMUM = 2000;
|
||||
|
||||
private final static Logger mLog = LoggerFactory.getLogger(ChannelRotationMonitor.class);
|
||||
|
||||
@@ -42,6 +42,7 @@ import javax.usb.UsbException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public abstract class FCDTunerController extends TunerController
|
||||
{
|
||||
@@ -60,6 +61,7 @@ public abstract class FCDTunerController extends TunerController
|
||||
|
||||
private FCDConfiguration mConfiguration = new FCDConfiguration();
|
||||
protected ComplexMixer mComplexMixer;
|
||||
protected ReentrantLock mLock = new ReentrantLock();
|
||||
|
||||
|
||||
/**
|
||||
@@ -302,6 +304,8 @@ public abstract class FCDTunerController extends TunerController
|
||||
*/
|
||||
public void setTunedFrequency(long frequency) throws SourceException
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_FREQUENCY_HZ, frequency);
|
||||
@@ -311,6 +315,10 @@ public abstract class FCDTunerController extends TunerController
|
||||
throw new SourceException("Couldn't set FCD Local " +
|
||||
"Oscillator Frequency [" + frequency + "]", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -176,10 +176,18 @@ public class FCD1TunerController extends FCDTunerController
|
||||
* @throws UsbException
|
||||
* @throws UsbClaimException
|
||||
*/
|
||||
public void setLNAGain(LNAGain gain)
|
||||
throws UsbException, UsbClaimException
|
||||
public void setLNAGain(LNAGain gain) throws UsbException, UsbClaimException
|
||||
{
|
||||
send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting());
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_LNA_GAIN, gain.getSetting());
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,7 +277,16 @@ public class FCD1TunerController extends FCDTunerController
|
||||
//Merge the results
|
||||
long correction = longGain | phase;
|
||||
|
||||
send(FCDCommand.APP_SET_IQ_CORRECTION, correction);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_IQ_CORRECTION, correction);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -402,15 +419,16 @@ public class FCD1TunerController extends FCDTunerController
|
||||
//Merge the results
|
||||
long correction = shiftedQuadrature | maskedInphase;
|
||||
|
||||
// Log.info( "FCD1: setting iq correction," +
|
||||
// " inphase:" + inphase +
|
||||
// " masked inphase:" + maskedInphase +
|
||||
// " quad:" + quadrature +
|
||||
// " masked quad:" + maskedQuadrature +
|
||||
// " shifted quadrature:" + shiftedQuadrature +
|
||||
// " correction:" + correction );
|
||||
mLock.lock();
|
||||
|
||||
send(FCDCommand.APP_SET_DC_CORRECTION, correction);
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_DC_CORRECTION, correction);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void getDCIQCorrection() throws UsbClaimException,
|
||||
@@ -464,10 +482,18 @@ public class FCD1TunerController extends FCDTunerController
|
||||
return mLNAEnhance;
|
||||
}
|
||||
|
||||
public void setLNAEnhance(LNAEnhance enhance)
|
||||
throws UsbClaimException, UsbException
|
||||
public void setLNAEnhance(LNAEnhance enhance) throws UsbClaimException, UsbException
|
||||
{
|
||||
send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting());
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_LNA_ENHANCE, enhance.getSetting());
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void getMixerGainSetting() throws UsbClaimException, UsbException
|
||||
@@ -488,7 +514,16 @@ public class FCD1TunerController extends FCDTunerController
|
||||
|
||||
public void setMixerGain(MixerGain gain) throws UsbClaimException, UsbException
|
||||
{
|
||||
send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting());
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_MIXER_GAIN, gain.getSetting());
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public enum Block
|
||||
|
||||
@@ -83,6 +83,8 @@ public class FCD2TunerController extends FCDTunerController
|
||||
|
||||
public void setLNAGain(boolean enabled) throws SourceException
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_LNA_GAIN, enabled ? 1 : 0);
|
||||
@@ -91,10 +93,16 @@ public class FCD2TunerController extends FCDTunerController
|
||||
{
|
||||
throw new SourceException("error while setting LNA Gain", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setMixerGain(boolean enabled) throws SourceException
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_MIXER_GAIN, enabled ? 1 : 0);
|
||||
@@ -103,6 +111,10 @@ public class FCD2TunerController extends FCDTunerController
|
||||
{
|
||||
throw new SourceException("error while setting Mixer Gain", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,6 +171,8 @@ public class FCD2TunerController extends FCDTunerController
|
||||
|
||||
public void setDCCorrection(int value)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_DC_CORRECTION, value);
|
||||
@@ -167,6 +181,10 @@ public class FCD2TunerController extends FCDTunerController
|
||||
{
|
||||
mLog.error("error setting dc correction to [" + value + "]", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public int getIQCorrection()
|
||||
@@ -191,6 +209,8 @@ public class FCD2TunerController extends FCDTunerController
|
||||
|
||||
public void setIQCorrection(int value)
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
send(FCDCommand.APP_SET_IQ_CORRECTION, value);
|
||||
@@ -199,5 +219,9 @@ public class FCD2TunerController extends FCDTunerController
|
||||
{
|
||||
mLog.error("error setting IQ correction to [" + value + "]", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class HackRFTunerController extends USBTunerController
|
||||
{
|
||||
@@ -69,6 +70,7 @@ public class HackRFTunerController extends USBTunerController
|
||||
private Device mDevice;
|
||||
private DeviceDescriptor mDeviceDescriptor;
|
||||
private DeviceHandle mDeviceHandle;
|
||||
private ReentrantLock mLock = new ReentrantLock();
|
||||
|
||||
public HackRFTunerController(Device device, DeviceDescriptor descriptor) throws SourceException
|
||||
{
|
||||
@@ -246,9 +248,16 @@ public class HackRFTunerController extends USBTunerController
|
||||
*/
|
||||
public void setBasebandFilter(BasebandFilter filter) throws UsbException
|
||||
{
|
||||
write(Request.BASEBAND_FILTER_BANDWIDTH_SET,
|
||||
filter.getLowValue(),
|
||||
filter.getHighValue());
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
write(Request.BASEBAND_FILTER_BANDWIDTH_SET, filter.getLowValue(), filter.getHighValue());
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -256,9 +265,17 @@ public class HackRFTunerController extends USBTunerController
|
||||
*/
|
||||
public void setAmplifierEnabled(boolean enabled) throws UsbException
|
||||
{
|
||||
write(Request.AMP_ENABLE, (enabled ? 1 : 0), 0);
|
||||
mLock.lock();
|
||||
|
||||
mAmplifierEnabled = enabled;
|
||||
try
|
||||
{
|
||||
write(Request.AMP_ENABLE, (enabled ? 1 : 0), 0);
|
||||
mAmplifierEnabled = enabled;
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean getAmplifier()
|
||||
@@ -271,11 +288,18 @@ public class HackRFTunerController extends USBTunerController
|
||||
*/
|
||||
public void setLNAGain(HackRFLNAGain gain) throws UsbException
|
||||
{
|
||||
int result = readByte(Request.SET_LNA_GAIN, 0, gain.getValue(), true);
|
||||
|
||||
if(result != 1)
|
||||
try
|
||||
{
|
||||
throw new UsbException("couldn't set lna gain to " + gain);
|
||||
int result = readByte(Request.SET_LNA_GAIN, 0, gain.getValue(), true);
|
||||
|
||||
if(result != 1)
|
||||
{
|
||||
throw new UsbException("couldn't set lna gain to " + gain);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,11 +308,20 @@ public class HackRFTunerController extends USBTunerController
|
||||
*/
|
||||
public void setVGAGain(HackRFVGAGain gain) throws UsbException
|
||||
{
|
||||
int result = readByte(Request.SET_VGA_GAIN, 0, gain.getValue(), true);
|
||||
mLock.lock();
|
||||
|
||||
if(result != 1)
|
||||
try
|
||||
{
|
||||
throw new UsbException("couldn't set vga gain to " + gain);
|
||||
int result = readByte(Request.SET_VGA_GAIN, 0, gain.getValue(), true);
|
||||
|
||||
if(result != 1)
|
||||
{
|
||||
throw new UsbException("couldn't set vga gain to " + gain);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -303,27 +336,34 @@ public class HackRFTunerController extends USBTunerController
|
||||
@Override
|
||||
public void setTunedFrequency(long frequency) throws SourceException
|
||||
{
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(8);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
int mhz = (int)(frequency / 1E6);
|
||||
int hz = (int)(frequency - (mhz * 1E6));
|
||||
|
||||
buffer.putInt(mhz);
|
||||
buffer.putInt(hz);
|
||||
|
||||
buffer.rewind();
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
write(Request.SET_FREQUENCY, 0, 0, buffer);
|
||||
}
|
||||
catch(UsbException e)
|
||||
{
|
||||
mLog.error("error setting frequency [" + frequency + "]", e);
|
||||
ByteBuffer buffer = ByteBuffer.allocateDirect(8);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
|
||||
throw new SourceException("error setting frequency [" +
|
||||
frequency + "]", e);
|
||||
int mhz = (int)(frequency / 1E6);
|
||||
int hz = (int)(frequency - (mhz * 1E6));
|
||||
|
||||
buffer.putInt(mhz);
|
||||
buffer.putInt(hz);
|
||||
|
||||
buffer.rewind();
|
||||
|
||||
try
|
||||
{
|
||||
write(Request.SET_FREQUENCY, 0, 0, buffer);
|
||||
}
|
||||
catch(UsbException e)
|
||||
{
|
||||
mLog.error("error setting frequency [" + frequency + "]", e);
|
||||
throw new SourceException("error setting frequency [" + frequency + "]", e);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ import java.nio.ByteOrder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public abstract class RTL2832TunerController extends USBTunerController
|
||||
{
|
||||
@@ -60,6 +61,7 @@ public abstract class RTL2832TunerController extends USBTunerController
|
||||
public final static long TIMEOUT_US = 1000000l; //uSeconds
|
||||
public final static byte REQUEST_ZERO = (byte) 0;
|
||||
public final static byte EEPROM_ADDRESS = (byte) 0xA0;
|
||||
protected ReentrantLock mLock = new ReentrantLock();
|
||||
|
||||
public final static byte[] sFIR_COEFFICIENTS =
|
||||
{
|
||||
|
||||
@@ -384,6 +384,8 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
actualFrequency = calculateActualFrequency(pll, z, x);
|
||||
}
|
||||
|
||||
mLock.lock();
|
||||
|
||||
/* Apply the actual frequency */
|
||||
try
|
||||
{
|
||||
@@ -428,6 +430,10 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
throw new SourceException("E4K tuner controller - error tuning "
|
||||
+ "frequency [" + frequency + "]", e);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private long calculateActualFrequency(PLL pll, byte z, int x)
|
||||
@@ -657,38 +663,46 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
* @param gain
|
||||
* @throws UsbException
|
||||
*/
|
||||
public void setLNAGain(E4KLNAGain gain, boolean controlI2CRepeater)
|
||||
throws UsbException
|
||||
public void setLNAGain(E4KLNAGain gain, boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
if(controlI2CRepeater)
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, true);
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, true);
|
||||
}
|
||||
|
||||
if(gain == E4KLNAGain.AUTOMATIC)
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC1,
|
||||
AGC1_MOD_MASK,
|
||||
AGCMode.IF_SERIAL_LNA_AUTON.getValue(),
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC1,
|
||||
AGC1_MOD_MASK,
|
||||
AGCMode.SERIAL.getValue(),
|
||||
false);
|
||||
|
||||
|
||||
writeMaskedE4KRegister(E4KLNAGain.getRegister(),
|
||||
E4KLNAGain.getMask(),
|
||||
gain.getValue(),
|
||||
false);
|
||||
}
|
||||
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, false);
|
||||
}
|
||||
}
|
||||
|
||||
if(gain == E4KLNAGain.AUTOMATIC)
|
||||
finally
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC1,
|
||||
AGC1_MOD_MASK,
|
||||
AGCMode.IF_SERIAL_LNA_AUTON.getValue(),
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC1,
|
||||
AGC1_MOD_MASK,
|
||||
AGCMode.SERIAL.getValue(),
|
||||
false);
|
||||
|
||||
|
||||
writeMaskedE4KRegister(E4KLNAGain.getRegister(),
|
||||
E4KLNAGain.getMask(),
|
||||
gain.getValue(),
|
||||
false);
|
||||
}
|
||||
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, false);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -699,8 +713,16 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
*/
|
||||
public E4KLNAGain getLNAGain(boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
return E4KLNAGain.fromRegisterValue(
|
||||
readE4KRegister(E4KLNAGain.getRegister(), controlI2CRepeater));
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
return E4KLNAGain.fromRegisterValue(readE4KRegister(E4KLNAGain.getRegister(), controlI2CRepeater));
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -711,13 +733,18 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
* @param gain
|
||||
* @throws UsbException
|
||||
*/
|
||||
public void setEnhanceGain(E4KEnhanceGain gain, boolean controlI2CRepeater)
|
||||
throws UsbException
|
||||
public void setEnhanceGain(E4KEnhanceGain gain, boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
writeMaskedE4KRegister(E4KEnhanceGain.getRegister(),
|
||||
E4KEnhanceGain.getMask(),
|
||||
gain.getValue(),
|
||||
controlI2CRepeater);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
writeMaskedE4KRegister(E4KEnhanceGain.getRegister(), E4KEnhanceGain.getMask(), gain.getValue(), controlI2CRepeater);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -728,72 +755,90 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
* @return
|
||||
* @throws UsbException
|
||||
*/
|
||||
public E4KEnhanceGain getEnhanceGain(boolean controlI2CRepeater)
|
||||
throws UsbException
|
||||
public E4KEnhanceGain getEnhanceGain(boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
return E4KEnhanceGain.fromRegisterValue(
|
||||
readE4KRegister(E4KEnhanceGain.getRegister(), controlI2CRepeater));
|
||||
}
|
||||
mLock.lock();
|
||||
|
||||
public void setMixerGain(E4KMixerGain gain, boolean controlI2CRepeater)
|
||||
throws UsbException
|
||||
{
|
||||
if(controlI2CRepeater)
|
||||
try
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, true);
|
||||
return E4KEnhanceGain.fromRegisterValue(readE4KRegister(E4KEnhanceGain.getRegister(), controlI2CRepeater));
|
||||
}
|
||||
|
||||
boolean localI2CRepeaterControl = false;
|
||||
|
||||
if(gain == E4KMixerGain.AUTOMATIC)
|
||||
finally
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC7,
|
||||
AGC7_MIXER_GAIN_MASK,
|
||||
AGC7_MIX_GAIN_AUTO,
|
||||
localI2CRepeaterControl);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC7,
|
||||
AGC7_MIXER_GAIN_MASK,
|
||||
AGC7_MIX_GAIN_MANUAL,
|
||||
localI2CRepeaterControl);
|
||||
|
||||
/* Set the desired manual gain setting */
|
||||
writeMaskedE4KRegister(E4KMixerGain.getRegister(),
|
||||
E4KMixerGain.getMask(),
|
||||
gain.getValue(),
|
||||
localI2CRepeaterControl);
|
||||
}
|
||||
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, false);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public E4KMixerGain getMixerGain(boolean controlI2CRepeater)
|
||||
throws UsbException
|
||||
public void setMixerGain(E4KMixerGain gain, boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
byte autoOrManual = readMaskedE4KRegister(Register.AGC7,
|
||||
AGC7_MIXER_GAIN_MASK,
|
||||
controlI2CRepeater);
|
||||
mLock.lock();
|
||||
|
||||
if(autoOrManual == AGC7_MIX_GAIN_AUTO)
|
||||
try
|
||||
{
|
||||
return E4KMixerGain.AUTOMATIC;
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, true);
|
||||
}
|
||||
|
||||
boolean localI2CRepeaterControl = false;
|
||||
|
||||
if(gain == E4KMixerGain.AUTOMATIC)
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC7,
|
||||
AGC7_MIXER_GAIN_MASK,
|
||||
AGC7_MIX_GAIN_AUTO,
|
||||
localI2CRepeaterControl);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeMaskedE4KRegister(Register.AGC7,
|
||||
AGC7_MIXER_GAIN_MASK,
|
||||
AGC7_MIX_GAIN_MANUAL,
|
||||
localI2CRepeaterControl);
|
||||
|
||||
/* Set the desired manual gain setting */
|
||||
writeMaskedE4KRegister(E4KMixerGain.getRegister(),
|
||||
E4KMixerGain.getMask(),
|
||||
gain.getValue(),
|
||||
localI2CRepeaterControl);
|
||||
}
|
||||
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
finally
|
||||
{
|
||||
int register = readE4KRegister(E4KMixerGain.getRegister(),
|
||||
controlI2CRepeater);
|
||||
|
||||
return E4KMixerGain.fromRegisterValue(register);
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setIFStage1Gain(IFStage1Gain gain,
|
||||
boolean controlI2CRepeater) throws UsbException
|
||||
public E4KMixerGain getMixerGain(boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
byte autoOrManual = readMaskedE4KRegister(Register.AGC7, AGC7_MIXER_GAIN_MASK, controlI2CRepeater);
|
||||
|
||||
if(autoOrManual == AGC7_MIX_GAIN_AUTO)
|
||||
{
|
||||
return E4KMixerGain.AUTOMATIC;
|
||||
}
|
||||
else
|
||||
{
|
||||
int register = readE4KRegister(E4KMixerGain.getRegister(), controlI2CRepeater);
|
||||
return E4KMixerGain.fromRegisterValue(register);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void setIFStage1Gain(IFStage1Gain gain, boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
writeMaskedE4KRegister(IFStage1Gain.getRegister(),
|
||||
IFStage1Gain.getMask(),
|
||||
@@ -959,8 +1004,7 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
}
|
||||
}
|
||||
|
||||
public void setBand(long frequency,
|
||||
boolean controlI2CRepeater) throws UsbException
|
||||
public void setBand(long frequency, boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
if(controlI2CRepeater)
|
||||
{
|
||||
@@ -997,8 +1041,7 @@ public class E4KTunerController extends RTL2832TunerController
|
||||
}
|
||||
}
|
||||
|
||||
private void setRFFilter(long frequency,
|
||||
boolean controlI2CRepeater) throws UsbException
|
||||
private void setRFFilter(long frequency, boolean controlI2CRepeater) throws UsbException
|
||||
{
|
||||
RFFilter filter = RFFilter.fromFrequency(frequency);
|
||||
|
||||
|
||||
@@ -180,6 +180,8 @@ public class R820TTunerController extends RTL2832TunerController
|
||||
@Override
|
||||
public void setTunedFrequency(long frequency) throws SourceException
|
||||
{
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
enableI2CRepeater(mDeviceHandle, true);
|
||||
@@ -200,6 +202,10 @@ public class R820TTunerController extends RTL2832TunerController
|
||||
+ "while setting frequency [" + frequency + "] - " +
|
||||
e.getLocalizedMessage());
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -693,7 +699,16 @@ public class R820TTunerController extends RTL2832TunerController
|
||||
*/
|
||||
public void setLNAGain(R820TLNAGain gain, boolean controlI2C) throws UsbException
|
||||
{
|
||||
writeR820TRegister(Register.LNA_GAIN, gain.getSetting(), controlI2C);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
writeR820TRegister(Register.LNA_GAIN, gain.getSetting(), controlI2C);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -701,7 +716,16 @@ public class R820TTunerController extends RTL2832TunerController
|
||||
*/
|
||||
public void setMixerGain(R820TMixerGain gain, boolean controlI2C) throws UsbException
|
||||
{
|
||||
writeR820TRegister(Register.MIXER_GAIN, gain.getSetting(), controlI2C);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
writeR820TRegister(Register.MIXER_GAIN, gain.getSetting(), controlI2C);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -709,7 +733,16 @@ public class R820TTunerController extends RTL2832TunerController
|
||||
*/
|
||||
public void setVGAGain(R820TVGAGain gain, boolean controlI2C) throws UsbException
|
||||
{
|
||||
writeR820TRegister(Register.VGA_GAIN, gain.getSetting(), controlI2C);
|
||||
mLock.lock();
|
||||
|
||||
try
|
||||
{
|
||||
writeR820TRegister(Register.VGA_GAIN, gain.getSetting(), controlI2C);
|
||||
}
|
||||
finally
|
||||
{
|
||||
mLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,9 +25,10 @@ import com.jidesoft.swing.JideSplitPane;
|
||||
import io.github.dsheirer.controller.channel.Channel;
|
||||
import io.github.dsheirer.controller.channel.ChannelModel;
|
||||
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
|
||||
import io.github.dsheirer.controller.channel.ChannelUtils;
|
||||
import io.github.dsheirer.dsp.filter.Window.WindowType;
|
||||
import io.github.dsheirer.dsp.filter.smoothing.SmoothingFilter.SmoothingType;
|
||||
import io.github.dsheirer.eventbus.MyEventBus;
|
||||
import io.github.dsheirer.gui.playlist.channel.ViewChannelRequest;
|
||||
import io.github.dsheirer.playlist.PlaylistManager;
|
||||
import io.github.dsheirer.properties.SystemProperties;
|
||||
import io.github.dsheirer.sample.Listener;
|
||||
@@ -635,26 +636,22 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ReusableCom
|
||||
|
||||
if(event.getComponent() == mOverlayPanel)
|
||||
{
|
||||
ArrayList<Channel> channels =
|
||||
mOverlayPanel.getChannelsAtFrequency(frequency);
|
||||
ArrayList<Channel> channels = mOverlayPanel.getChannelsAtFrequency(frequency);
|
||||
|
||||
JMenu channelMenu = new JMenu("Channels");
|
||||
for(Channel channel : channels)
|
||||
{
|
||||
JMenu channelMenu = ChannelUtils.getContextMenu(mChannelModel,
|
||||
mChannelProcessingManager, channel,
|
||||
SpectralDisplayPanel.this);
|
||||
|
||||
if(channelMenu != null)
|
||||
{
|
||||
contextMenu.add(channelMenu);
|
||||
}
|
||||
JMenuItem viewChannel = new JMenuItem("View/Edit: " + channel.getShortTitle());
|
||||
viewChannel.addActionListener(e -> MyEventBus.getGlobalEventBus().post(new ViewChannelRequest(channel)));
|
||||
channelMenu.add(viewChannel);
|
||||
}
|
||||
|
||||
contextMenu.add(channelMenu);
|
||||
|
||||
if(!channels.isEmpty())
|
||||
{
|
||||
contextMenu.add(new JSeparator());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user