mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-14 17:47:32 -05:00
61 Rotating Control Channel Support - P25 Decoder (#475)
* Initial work on add support for rotating control channels. * Creates multiple frequency editor control. * Updates channel source editor to allow user to select multi-frequency configuration and add multiple frequencies. * Adds full support for rotating control channels to the P25 channelizer. Users define multiple frequencies in the channel source configuration. The decoder monitors the state of the channel for a CONTROL state. If the state does not stay in the CONTROL state for a user-specified delay value, then the decoder rotates to the next frequency in the list. This behavior continues until the decoder state once again changes to CONTROL. This is NOT a 'scanner' feature.
This commit is contained in:
21
build.gradle
21
build.gradle
@@ -1,3 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 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/>
|
||||||
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
jcenter()
|
jcenter()
|
||||||
@@ -55,6 +75,7 @@ dependencies {
|
|||||||
compile 'org.apache.axis:axis:1.4'
|
compile 'org.apache.axis:axis:1.4'
|
||||||
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.3'
|
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.9.3'
|
||||||
compile 'org.kordamp.ikonli:ikonli-javafx:2.1.1'
|
compile 'org.kordamp.ikonli:ikonli-javafx:2.1.1'
|
||||||
|
compile 'org.kordamp.ikonli:ikonli-swing:2.1.1'
|
||||||
compile 'org.kordamp.ikonli:ikonli-fontawesome-pack:2.1.1'
|
compile 'org.kordamp.ikonli:ikonli-fontawesome-pack:2.1.1'
|
||||||
compile 'com.google.guava:guava:27.0-jre'
|
compile 'com.google.guava:guava:27.0-jre'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -33,8 +33,8 @@ import io.github.dsheirer.identifier.configuration.DecoderTypeConfigurationIdent
|
|||||||
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
||||||
import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier;
|
||||||
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
|
||||||
|
import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier;
|
||||||
import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier;
|
import io.github.dsheirer.identifier.decoder.DecoderLogicalChannelNameIdentifier;
|
||||||
import io.github.dsheirer.identifier.decoder.DecoderStateIdentifier;
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -54,7 +54,7 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
private SiteConfigurationIdentifier mSiteConfigurationIdentifier;
|
private SiteConfigurationIdentifier mSiteConfigurationIdentifier;
|
||||||
private ChannelNameConfigurationIdentifier mChannelNameConfigurationIdentifier;
|
private ChannelNameConfigurationIdentifier mChannelNameConfigurationIdentifier;
|
||||||
private FrequencyConfigurationIdentifier mFrequencyConfigurationIdentifier;
|
private FrequencyConfigurationIdentifier mFrequencyConfigurationIdentifier;
|
||||||
private DecoderStateIdentifier mDecoderStateIdentifier = DecoderStateIdentifier.IDLE;
|
private ChannelStateIdentifier mChannelStateIdentifier = ChannelStateIdentifier.IDLE;
|
||||||
private DecoderLogicalChannelNameIdentifier mDecoderLogicalChannelNameIdentifier;
|
private DecoderLogicalChannelNameIdentifier mDecoderLogicalChannelNameIdentifier;
|
||||||
private DecoderTypeConfigurationIdentifier mDecoderTypeConfigurationIdentifier;
|
private DecoderTypeConfigurationIdentifier mDecoderTypeConfigurationIdentifier;
|
||||||
private Identifier mFromIdentifier;
|
private Identifier mFromIdentifier;
|
||||||
@@ -145,14 +145,14 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
/**
|
/**
|
||||||
* Decoder state identifier
|
* Decoder state identifier
|
||||||
*/
|
*/
|
||||||
public DecoderStateIdentifier getDecoderStateIdentifier()
|
public ChannelStateIdentifier getChannelStateIdentifier()
|
||||||
{
|
{
|
||||||
return mDecoderStateIdentifier;
|
return mChannelStateIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasDecoderStateIdentifier()
|
public boolean hasDecoderStateIdentifier()
|
||||||
{
|
{
|
||||||
return mDecoderStateIdentifier != null;
|
return mChannelStateIdentifier != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -250,7 +250,7 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
switch(identifier.getForm())
|
switch(identifier.getForm())
|
||||||
{
|
{
|
||||||
case ALIAS_LIST:
|
case ALIAS_LIST:
|
||||||
if(update.isAdded())
|
if(update.isAdd())
|
||||||
{
|
{
|
||||||
mAliasList = mAliasModel.getAliasList((AliasListConfigurationIdentifier)identifier);
|
mAliasList = mAliasModel.getAliasList((AliasListConfigurationIdentifier)identifier);
|
||||||
|
|
||||||
@@ -268,23 +268,58 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case CHANNEL:
|
case CHANNEL:
|
||||||
mChannelNameConfigurationIdentifier = update.isAdded() ? (ChannelNameConfigurationIdentifier)identifier : null;
|
if(identifier instanceof ChannelNameConfigurationIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mChannelNameConfigurationIdentifier = (ChannelNameConfigurationIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mChannelNameConfigurationIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.CONFIGURATION_CHANNEL);
|
broadcastUpdate(ChannelMetadataField.CONFIGURATION_CHANNEL);
|
||||||
break;
|
break;
|
||||||
case CHANNEL_FREQUENCY:
|
case CHANNEL_FREQUENCY:
|
||||||
mFrequencyConfigurationIdentifier = update.isAdded() ? (FrequencyConfigurationIdentifier)identifier : null;
|
if(identifier instanceof FrequencyConfigurationIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mFrequencyConfigurationIdentifier = (FrequencyConfigurationIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFrequencyConfigurationIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.CONFIGURATION_FREQUENCY);
|
broadcastUpdate(ChannelMetadataField.CONFIGURATION_FREQUENCY);
|
||||||
break;
|
break;
|
||||||
case DECODER_TYPE:
|
case DECODER_TYPE:
|
||||||
mDecoderTypeConfigurationIdentifier = update.isAdded() ? (DecoderTypeConfigurationIdentifier)identifier : null;
|
if(identifier instanceof DecoderTypeConfigurationIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mDecoderTypeConfigurationIdentifier = (DecoderTypeConfigurationIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDecoderTypeConfigurationIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.DECODER_TYPE);
|
broadcastUpdate(ChannelMetadataField.DECODER_TYPE);
|
||||||
break;
|
break;
|
||||||
case SITE:
|
case SITE:
|
||||||
mSiteConfigurationIdentifier = update.isAdded() ? (SiteConfigurationIdentifier)identifier : null;
|
if(identifier instanceof SiteConfigurationIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mSiteConfigurationIdentifier = (SiteConfigurationIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSiteConfigurationIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.CONFIGURATION_SITE);
|
broadcastUpdate(ChannelMetadataField.CONFIGURATION_SITE);
|
||||||
break;
|
break;
|
||||||
case SYSTEM:
|
case SYSTEM:
|
||||||
mSystemConfigurationIdentifier = update.isAdded() ? (SystemConfigurationIdentifier)identifier : null;
|
if(identifier instanceof SystemConfigurationIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mSystemConfigurationIdentifier = (SystemConfigurationIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mSystemConfigurationIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.CONFIGURATION_SYSTEM);
|
broadcastUpdate(ChannelMetadataField.CONFIGURATION_SYSTEM);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -293,11 +328,25 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
switch(identifier.getForm())
|
switch(identifier.getForm())
|
||||||
{
|
{
|
||||||
case CHANNEL_NAME:
|
case CHANNEL_NAME:
|
||||||
mDecoderLogicalChannelNameIdentifier = update.isAdded() ? (DecoderLogicalChannelNameIdentifier)identifier : null;
|
if(identifier instanceof DecoderLogicalChannelNameIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mDecoderLogicalChannelNameIdentifier = (DecoderLogicalChannelNameIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mDecoderLogicalChannelNameIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.DECODER_CHANNEL_NAME);
|
broadcastUpdate(ChannelMetadataField.DECODER_CHANNEL_NAME);
|
||||||
break;
|
break;
|
||||||
case STATE:
|
case STATE:
|
||||||
mDecoderStateIdentifier = update.isAdded() ? (DecoderStateIdentifier)identifier : null;
|
if(identifier instanceof ChannelStateIdentifier && (update.isAdd() || update.isSilentAdd()))
|
||||||
|
{
|
||||||
|
mChannelStateIdentifier = (ChannelStateIdentifier)identifier;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mChannelStateIdentifier = null;
|
||||||
|
}
|
||||||
broadcastUpdate(ChannelMetadataField.DECODER_STATE);
|
broadcastUpdate(ChannelMetadataField.DECODER_STATE);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -305,7 +354,7 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
case USER:
|
case USER:
|
||||||
if(identifier.getRole() == Role.FROM)
|
if(identifier.getRole() == Role.FROM)
|
||||||
{
|
{
|
||||||
mFromIdentifier = update.isAdded() ? identifier : null;
|
mFromIdentifier = update.isAdd() ? identifier : null;
|
||||||
|
|
||||||
if(mAliasList != null && mFromIdentifier != null)
|
if(mAliasList != null && mFromIdentifier != null)
|
||||||
{
|
{
|
||||||
@@ -320,7 +369,7 @@ public class ChannelMetadata implements Listener<IdentifierUpdateNotification>,
|
|||||||
}
|
}
|
||||||
else if(identifier.getRole() == Role.TO)
|
else if(identifier.getRole() == Role.TO)
|
||||||
{
|
{
|
||||||
mToIdentifier = update.isAdded() ? identifier : null;
|
mToIdentifier = update.isAdd() ? identifier : null;
|
||||||
|
|
||||||
if(mAliasList != null && mToIdentifier != null)
|
if(mAliasList != null && mToIdentifier != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -176,7 +176,7 @@ public class ChannelMetadataModel extends AbstractTableModel implements IChannel
|
|||||||
switch(columnIndex)
|
switch(columnIndex)
|
||||||
{
|
{
|
||||||
case COLUMN_DECODER_STATE:
|
case COLUMN_DECODER_STATE:
|
||||||
return channelMetadata.getDecoderStateIdentifier();
|
return channelMetadata.getChannelStateIdentifier();
|
||||||
case COLUMN_DECODER_TYPE:
|
case COLUMN_DECODER_TYPE:
|
||||||
return channelMetadata.getDecoderTypeConfigurationIdentifier();
|
return channelMetadata.getDecoderTypeConfigurationIdentifier();
|
||||||
case COLUMN_DECODER_LOGICAL_CHANNEL_NAME:
|
case COLUMN_DECODER_LOGICAL_CHANNEL_NAME:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -29,7 +29,7 @@ import io.github.dsheirer.controller.channel.ChannelUtils;
|
|||||||
import io.github.dsheirer.icon.IconManager;
|
import io.github.dsheirer.icon.IconManager;
|
||||||
import io.github.dsheirer.identifier.Identifier;
|
import io.github.dsheirer.identifier.Identifier;
|
||||||
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
||||||
import io.github.dsheirer.identifier.decoder.DecoderStateIdentifier;
|
import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier;
|
||||||
import io.github.dsheirer.module.ProcessingChain;
|
import io.github.dsheirer.module.ProcessingChain;
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
|
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
|
||||||
@@ -347,9 +347,9 @@ public class ChannelMetadataPanel extends JPanel implements ListSelectionListene
|
|||||||
Color background = table.getBackground();
|
Color background = table.getBackground();
|
||||||
Color foreground = table.getForeground();
|
Color foreground = table.getForeground();
|
||||||
|
|
||||||
if(value instanceof DecoderStateIdentifier)
|
if(value instanceof ChannelStateIdentifier)
|
||||||
{
|
{
|
||||||
State state = ((DecoderStateIdentifier)value).getValue();
|
State state = ((ChannelStateIdentifier)value).getValue();
|
||||||
label.setText(state.getDisplayValue());
|
label.setText(state.getDisplayValue());
|
||||||
|
|
||||||
if(mBackgroundColors.containsKey(state))
|
if(mBackgroundColors.containsKey(state))
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,8 +15,8 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.channel.state;
|
package io.github.dsheirer.channel.state;
|
||||||
|
|
||||||
import io.github.dsheirer.alias.AliasModel;
|
import io.github.dsheirer.alias.AliasModel;
|
||||||
@@ -37,7 +38,7 @@ import io.github.dsheirer.identifier.configuration.DecoderTypeConfigurationIdent
|
|||||||
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.FrequencyConfigurationIdentifier;
|
||||||
import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.SiteConfigurationIdentifier;
|
||||||
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
|
import io.github.dsheirer.identifier.configuration.SystemConfigurationIdentifier;
|
||||||
import io.github.dsheirer.identifier.decoder.DecoderStateIdentifier;
|
import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier;
|
||||||
import io.github.dsheirer.module.Module;
|
import io.github.dsheirer.module.Module;
|
||||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||||
@@ -49,12 +50,14 @@ import io.github.dsheirer.source.ISourceEventProvider;
|
|||||||
import io.github.dsheirer.source.SourceEvent;
|
import io.github.dsheirer.source.SourceEvent;
|
||||||
import io.github.dsheirer.source.SourceType;
|
import io.github.dsheirer.source.SourceType;
|
||||||
import io.github.dsheirer.source.config.SourceConfigTuner;
|
import io.github.dsheirer.source.config.SourceConfigTuner;
|
||||||
|
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
|
||||||
import io.github.dsheirer.source.heartbeat.Heartbeat;
|
import io.github.dsheirer.source.heartbeat.Heartbeat;
|
||||||
import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
|
import io.github.dsheirer.source.heartbeat.IHeartbeatListener;
|
||||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ChannelState extends Module implements IChannelEventProvider, IDecodeEventProvider,
|
public class ChannelState extends Module implements IChannelEventProvider, IDecodeEventProvider,
|
||||||
IDecoderStateEventListener, IDecoderStateEventProvider, ISourceEventListener, ISourceEventProvider,
|
IDecoderStateEventListener, IDecoderStateEventProvider, ISourceEventListener, ISourceEventProvider,
|
||||||
IHeartbeatListener, ISquelchStateProvider, IOverflowListener, IdentifierUpdateListener, IdentifierUpdateProvider
|
IHeartbeatListener, ISquelchStateProvider, IOverflowListener, IdentifierUpdateListener, IdentifierUpdateProvider
|
||||||
@@ -76,6 +79,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
private Channel mChannel;
|
private Channel mChannel;
|
||||||
private SourceEventListener mInternalSourceEventListener;
|
private SourceEventListener mInternalSourceEventListener;
|
||||||
private ChannelMetadata mChannelMetadata;
|
private ChannelMetadata mChannelMetadata;
|
||||||
|
private IdentifierUpdateNotificationProxy mIdentifierUpdateNotificationProxy = new IdentifierUpdateNotificationProxy();
|
||||||
|
|
||||||
private boolean mSquelchLocked = false;
|
private boolean mSquelchLocked = false;
|
||||||
private boolean mSelected = false;
|
private boolean mSelected = false;
|
||||||
@@ -108,6 +112,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
{
|
{
|
||||||
mChannel = channel;
|
mChannel = channel;
|
||||||
mChannelMetadata = new ChannelMetadata(aliasModel);
|
mChannelMetadata = new ChannelMetadata(aliasModel);
|
||||||
|
mIdentifierCollection.setIdentifierUpdateListener(mIdentifierUpdateNotificationProxy);
|
||||||
createConfigurationIdentifiers(channel);
|
createConfigurationIdentifiers(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,8 +141,17 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
}
|
}
|
||||||
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
|
if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER)
|
||||||
{
|
{
|
||||||
TunerChannel tunerChannel = ((SourceConfigTuner)channel.getSourceConfiguration()).getTunerChannel();
|
long frequency = ((SourceConfigTuner)channel.getSourceConfiguration()).getFrequency();
|
||||||
mIdentifierCollection.update(FrequencyConfigurationIdentifier.create(tunerChannel.getFrequency()));
|
mIdentifierCollection.update(FrequencyConfigurationIdentifier.create(frequency));
|
||||||
|
}
|
||||||
|
else if(channel.getSourceConfiguration().getSourceType() == SourceType.TUNER_MULTIPLE_FREQUENCIES)
|
||||||
|
{
|
||||||
|
List<Long> frequencies = ((SourceConfigTunerMultipleFrequency)channel.getSourceConfiguration()).getFrequencies();
|
||||||
|
|
||||||
|
if(frequencies.size() > 0)
|
||||||
|
{
|
||||||
|
mIdentifierCollection.update(FrequencyConfigurationIdentifier.create(frequencies.get(0)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,13 +178,13 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
@Override
|
@Override
|
||||||
public void setIdentifierUpdateListener(Listener<IdentifierUpdateNotification> listener)
|
public void setIdentifierUpdateListener(Listener<IdentifierUpdateNotification> listener)
|
||||||
{
|
{
|
||||||
mIdentifierCollection.setIdentifierUpdateListener(listener);
|
mIdentifierUpdateNotificationProxy.setListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeIdentifierUpdateListener()
|
public void removeIdentifierUpdateListener()
|
||||||
{
|
{
|
||||||
mIdentifierCollection.removeIdentifierUpdateListener();
|
mIdentifierUpdateNotificationProxy.removeListener();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,7 +203,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
{
|
{
|
||||||
mState = State.IDLE;
|
mState = State.IDLE;
|
||||||
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
|
broadcast(new DecoderStateEvent(this, Event.RESET, State.IDLE));
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.IDLE);
|
mIdentifierCollection.update(ChannelStateIdentifier.IDLE);
|
||||||
mSourceOverflow = false;
|
mSourceOverflow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +372,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
mState = state;
|
mState = state;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.ACTIVE);
|
mIdentifierCollection.update(ChannelStateIdentifier.ACTIVE);
|
||||||
break;
|
break;
|
||||||
case CONTROL:
|
case CONTROL:
|
||||||
//Don't allow traffic channels to be control channels, otherwise they can't transition to teardown
|
//Don't allow traffic channels to be control channels, otherwise they can't transition to teardown
|
||||||
@@ -367,39 +381,41 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
mState = state;
|
mState = state;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.CONTROL);
|
mIdentifierCollection.update(ChannelStateIdentifier.CONTROL);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DATA:
|
case DATA:
|
||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
mState = state;
|
mState = state;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.DATA);
|
mIdentifierCollection.update(ChannelStateIdentifier.DATA);
|
||||||
break;
|
break;
|
||||||
case ENCRYPTED:
|
case ENCRYPTED:
|
||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
mState = state;
|
mState = state;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.ENCRYPTED);
|
mIdentifierCollection.update(ChannelStateIdentifier.ENCRYPTED);
|
||||||
break;
|
break;
|
||||||
case CALL:
|
case CALL:
|
||||||
broadcast(SquelchState.UNSQUELCH);
|
broadcast(SquelchState.UNSQUELCH);
|
||||||
updateFadeTimeout();
|
updateFadeTimeout();
|
||||||
mState = state;
|
mState = state;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.CALL);
|
mIdentifierCollection.update(ChannelStateIdentifier.CALL);
|
||||||
break;
|
break;
|
||||||
case FADE:
|
case FADE:
|
||||||
processFadeState();
|
processFadeState();
|
||||||
|
mIdentifierCollection.update(ChannelStateIdentifier.FADE);
|
||||||
break;
|
break;
|
||||||
case IDLE:
|
case IDLE:
|
||||||
processIdleState();
|
processIdleState();
|
||||||
|
mIdentifierCollection.update(ChannelStateIdentifier.IDLE);
|
||||||
break;
|
break;
|
||||||
case TEARDOWN:
|
case TEARDOWN:
|
||||||
processTeardownState();
|
processTeardownState();
|
||||||
break;
|
break;
|
||||||
case RESET:
|
case RESET:
|
||||||
mState = State.IDLE;
|
mState = State.IDLE;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.IDLE);
|
mIdentifierCollection.update(ChannelStateIdentifier.IDLE);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@@ -447,7 +463,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
{
|
{
|
||||||
updateResetTimeout();
|
updateResetTimeout();
|
||||||
mState = State.FADE;
|
mState = State.FADE;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.FADE);
|
mIdentifierCollection.update(ChannelStateIdentifier.FADE);
|
||||||
|
|
||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
}
|
}
|
||||||
@@ -462,7 +478,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
}
|
}
|
||||||
|
|
||||||
mState = State.IDLE;
|
mState = State.IDLE;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.IDLE);
|
mIdentifierCollection.update(ChannelStateIdentifier.IDLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processTeardownState()
|
private void processTeardownState()
|
||||||
@@ -470,7 +486,7 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
broadcast(SquelchState.SQUELCH);
|
broadcast(SquelchState.SQUELCH);
|
||||||
|
|
||||||
mState = State.TEARDOWN;
|
mState = State.TEARDOWN;
|
||||||
mIdentifierCollection.update(DecoderStateIdentifier.TEARDOWN);
|
mIdentifierCollection.update(ChannelStateIdentifier.TEARDOWN);
|
||||||
|
|
||||||
if(mChannel.isTrafficChannel())
|
if(mChannel.isTrafficChannel())
|
||||||
{
|
{
|
||||||
@@ -591,6 +607,12 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
//Rebroadcast source frequency change events for the decoder(s) to process
|
//Rebroadcast source frequency change events for the decoder(s) to process
|
||||||
long frequency = sourceEvent.getValue().longValue();
|
long frequency = sourceEvent.getValue().longValue();
|
||||||
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
|
broadcast(new DecoderStateEvent(this, Event.SOURCE_FREQUENCY, getState(), frequency));
|
||||||
|
|
||||||
|
//Create a new frequency configuration identifier so that downstream consumers receive the change
|
||||||
|
//via channel metadata and audio packet updates - this is a silent add that is sent as a notification
|
||||||
|
//to all identifier collections so that they don't rebroadcast the change and cause a feedback loop
|
||||||
|
mIdentifierUpdateNotificationProxy.receive(new IdentifierUpdateNotification(
|
||||||
|
FrequencyConfigurationIdentifier.create(frequency), IdentifierUpdateNotification.Operation.SILENT_ADD));
|
||||||
break;
|
break;
|
||||||
case NOTIFICATION_MEASURED_FREQUENCY_ERROR:
|
case NOTIFICATION_MEASURED_FREQUENCY_ERROR:
|
||||||
//Rebroadcast frequency error measurements to external listener if we're currently
|
//Rebroadcast frequency error measurements to external listener if we're currently
|
||||||
@@ -707,4 +729,32 @@ public class ChannelState extends Module implements IChannelEventProvider, IDeco
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Proxy between the identifier collection and the external update notification listener. This proxy enables
|
||||||
|
* access to internal components to broadcast silent identifier update notifications externally.
|
||||||
|
*/
|
||||||
|
public class IdentifierUpdateNotificationProxy implements Listener<IdentifierUpdateNotification>
|
||||||
|
{
|
||||||
|
private Listener<IdentifierUpdateNotification> mIdentifierUpdateNotificationListener;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void receive(IdentifierUpdateNotification identifierUpdateNotification)
|
||||||
|
{
|
||||||
|
if(mIdentifierUpdateNotificationListener != null)
|
||||||
|
{
|
||||||
|
mIdentifierUpdateNotificationListener.receive(identifierUpdateNotification);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(Listener<IdentifierUpdateNotification> listener)
|
||||||
|
{
|
||||||
|
mIdentifierUpdateNotificationListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener()
|
||||||
|
{
|
||||||
|
mIdentifierUpdateNotificationListener = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,23 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 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.channel.state;
|
package io.github.dsheirer.channel.state;
|
||||||
|
|
||||||
import io.github.dsheirer.channel.IChannelDescriptor;
|
import io.github.dsheirer.channel.IChannelDescriptor;
|
||||||
@@ -261,7 +281,14 @@ public abstract class DecoderState extends Module implements ActivitySummaryProv
|
|||||||
identifierUpdateNotification.getIdentifier().getForm() != Form.DECODER_TYPE &&
|
identifierUpdateNotification.getIdentifier().getForm() != Form.DECODER_TYPE &&
|
||||||
identifierUpdateNotification.getIdentifier().getForm() != Form.CHANNEL_DESCRIPTOR)
|
identifierUpdateNotification.getIdentifier().getForm() != Form.CHANNEL_DESCRIPTOR)
|
||||||
{
|
{
|
||||||
getIdentifierCollection().update(identifierUpdateNotification.getIdentifier());
|
if(identifierUpdateNotification.isAdd())
|
||||||
|
{
|
||||||
|
getIdentifierCollection().update(identifierUpdateNotification.getIdentifier());
|
||||||
|
}
|
||||||
|
else if(identifierUpdateNotification.isSilentAdd())
|
||||||
|
{
|
||||||
|
getIdentifierCollection().silentUpdate(identifierUpdateNotification.getIdentifier());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(identifierUpdateNotification.getOperation() == IdentifierUpdateNotification.Operation.ADD)
|
if(identifierUpdateNotification.getOperation() == IdentifierUpdateNotification.Operation.ADD)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -36,6 +36,7 @@ import io.github.dsheirer.module.decode.DecoderFactory;
|
|||||||
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
import io.github.dsheirer.module.decode.event.IDecodeEvent;
|
||||||
import io.github.dsheirer.module.decode.event.MessageActivityModel;
|
import io.github.dsheirer.module.decode.event.MessageActivityModel;
|
||||||
import io.github.dsheirer.module.log.EventLogManager;
|
import io.github.dsheirer.module.log.EventLogManager;
|
||||||
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.record.RecorderManager;
|
import io.github.dsheirer.record.RecorderManager;
|
||||||
import io.github.dsheirer.record.RecorderType;
|
import io.github.dsheirer.record.RecorderType;
|
||||||
import io.github.dsheirer.record.binary.BinaryRecorder;
|
import io.github.dsheirer.record.binary.BinaryRecorder;
|
||||||
@@ -76,6 +77,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
|||||||
private RecorderManager mRecorderManager;
|
private RecorderManager mRecorderManager;
|
||||||
private SourceManager mSourceManager;
|
private SourceManager mSourceManager;
|
||||||
private AliasModel mAliasModel;
|
private AliasModel mAliasModel;
|
||||||
|
private UserPreferences mUserPreferences;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs the channel processing manager
|
* Constructs the channel processing manager
|
||||||
@@ -85,15 +87,18 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
|||||||
* @param recorderManager for receiving audio packets produced by the channel
|
* @param recorderManager for receiving audio packets produced by the channel
|
||||||
* @param sourceManager for obtaining a tuner channel source for the channel
|
* @param sourceManager for obtaining a tuner channel source for the channel
|
||||||
* @param aliasModel for aliasing of identifiers produced by the channel
|
* @param aliasModel for aliasing of identifiers produced by the channel
|
||||||
|
* @param userPreferences for user defined behavior and settings
|
||||||
*/
|
*/
|
||||||
public ChannelProcessingManager(ChannelMapModel channelMapModel, EventLogManager eventLogManager,
|
public ChannelProcessingManager(ChannelMapModel channelMapModel, EventLogManager eventLogManager,
|
||||||
RecorderManager recorderManager, SourceManager sourceManager, AliasModel aliasModel)
|
RecorderManager recorderManager, SourceManager sourceManager,
|
||||||
|
AliasModel aliasModel, UserPreferences userPreferences)
|
||||||
{
|
{
|
||||||
mChannelMapModel = channelMapModel;
|
mChannelMapModel = channelMapModel;
|
||||||
mEventLogManager = eventLogManager;
|
mEventLogManager = eventLogManager;
|
||||||
mRecorderManager = recorderManager;
|
mRecorderManager = recorderManager;
|
||||||
mSourceManager = sourceManager;
|
mSourceManager = sourceManager;
|
||||||
mAliasModel = aliasModel;
|
mAliasModel = aliasModel;
|
||||||
|
mUserPreferences = userPreferences;
|
||||||
mChannelMetadataModel = new ChannelMetadataModel();
|
mChannelMetadataModel = new ChannelMetadataModel();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +267,7 @@ public class ChannelProcessingManager implements Listener<ChannelEvent>
|
|||||||
processingChain.addFrequencyChangeListener(channel);
|
processingChain.addFrequencyChangeListener(channel);
|
||||||
|
|
||||||
/* Processing Modules */
|
/* Processing Modules */
|
||||||
List<Module> modules = DecoderFactory.getModules(mChannelMapModel, channel, mAliasModel);
|
List<Module> modules = DecoderFactory.getModules(mChannelMapModel, channel, mAliasModel, mUserPreferences);
|
||||||
processingChain.addModules(modules);
|
processingChain.addModules(modules);
|
||||||
|
|
||||||
/* Setup message activity model with filtering */
|
/* Setup message activity model with filtering */
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -154,7 +154,7 @@ public class SDRTrunk implements Listener<TunerEvent>
|
|||||||
mSourceManager = new SourceManager(tunerModel, mSettingsManager, mUserPreferences);
|
mSourceManager = new SourceManager(tunerModel, mSettingsManager, mUserPreferences);
|
||||||
|
|
||||||
mChannelProcessingManager = new ChannelProcessingManager(channelMapModel, eventLogManager, recorderManager,
|
mChannelProcessingManager = new ChannelProcessingManager(channelMapModel, eventLogManager, recorderManager,
|
||||||
mSourceManager, aliasModel);
|
mSourceManager, aliasModel, mUserPreferences);
|
||||||
|
|
||||||
mChannelModel.addListener(mChannelProcessingManager);
|
mChannelModel.addListener(mChannelProcessingManager);
|
||||||
mChannelProcessingManager.addChannelEventListener(mChannelModel);
|
mChannelProcessingManager.addChannelEventListener(mChannelModel);
|
||||||
|
|||||||
@@ -0,0 +1,494 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 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.gui.control;
|
||||||
|
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
import org.kordamp.ikonli.fontawesome.FontAwesome;
|
||||||
|
import org.kordamp.ikonli.swing.FontIcon;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.swing.AbstractAction;
|
||||||
|
import javax.swing.DefaultCellEditor;
|
||||||
|
import javax.swing.JButton;
|
||||||
|
import javax.swing.JFormattedTextField;
|
||||||
|
import javax.swing.JFrame;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JScrollPane;
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.JTextField;
|
||||||
|
import javax.swing.KeyStroke;
|
||||||
|
import javax.swing.ListSelectionModel;
|
||||||
|
import javax.swing.event.ListSelectionEvent;
|
||||||
|
import javax.swing.event.ListSelectionListener;
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import javax.swing.table.DefaultTableCellRenderer;
|
||||||
|
import javax.swing.text.DefaultFormatterFactory;
|
||||||
|
import javax.swing.text.NumberFormatter;
|
||||||
|
import java.awt.Component;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.awt.Rectangle;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.text.NumberFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple frequency editor control for editing an ordered list of frequencies. Frequency values are persisted as
|
||||||
|
* long values in hertz, but presented to the user as double values in megahertz.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class MultipleFrequencyEditor extends JPanel
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(MultipleFrequencyEditor.class);
|
||||||
|
private static final Long DEFAULT_FREQUENCY = 0l;
|
||||||
|
private DecimalFormat mDecimalFormat = new DecimalFormat("0.000000");
|
||||||
|
private FrequencyModel mFrequencyModel = new FrequencyModel();
|
||||||
|
private JTable mFrequencyTable;
|
||||||
|
private JButton mAddButton;
|
||||||
|
private JButton mRemoveButton;
|
||||||
|
private JButton mMoveUpButton;
|
||||||
|
private JButton mMoveDownButton;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the editor
|
||||||
|
* @param minimum allowable frequency value
|
||||||
|
* @param maximum allowable frequency value
|
||||||
|
*/
|
||||||
|
public MultipleFrequencyEditor(double minimum, double maximum)
|
||||||
|
{
|
||||||
|
setLayout(new MigLayout("insets 0 0 0 0", "[]1[grow,fill]1[]", "[]1[]1[top,grow,fill]"));
|
||||||
|
|
||||||
|
mMoveUpButton = new JButton();
|
||||||
|
mMoveUpButton.setIcon(FontIcon.of(FontAwesome.ARROW_UP, 10));
|
||||||
|
mMoveUpButton.setEnabled(false);
|
||||||
|
mMoveUpButton.addActionListener(e -> {
|
||||||
|
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||||
|
int index = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||||
|
mFrequencyModel.moveUp(index);
|
||||||
|
mFrequencyTable.getSelectionModel().setLeadSelectionIndex(index - 1);
|
||||||
|
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(selectedRow, 0, true)));
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
add(mMoveUpButton);
|
||||||
|
|
||||||
|
mFrequencyTable = new JTable(mFrequencyModel);
|
||||||
|
//testing ...
|
||||||
|
mFrequencyTable.setPreferredScrollableViewportSize(new Dimension(100, 65));
|
||||||
|
mFrequencyTable.setFillsViewportHeight(true);
|
||||||
|
|
||||||
|
mFrequencyTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||||
|
mFrequencyTable.getSelectionModel().addListSelectionListener(new SelectionListener());
|
||||||
|
mFrequencyTable.setDefaultEditor(Long.class, new FrequencyEditor(minimum, maximum));
|
||||||
|
mFrequencyTable.setDefaultRenderer(Long.class, new FrequencyRenderer());
|
||||||
|
JScrollPane listScrollPane = new JScrollPane(mFrequencyTable);
|
||||||
|
add(listScrollPane, "span 1 3");
|
||||||
|
|
||||||
|
mAddButton = new JButton();
|
||||||
|
mAddButton.setIcon(FontIcon.of(FontAwesome.PLUS, 10));
|
||||||
|
mAddButton.addActionListener(e -> {
|
||||||
|
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||||
|
if(selectedRow >= 0)
|
||||||
|
{
|
||||||
|
int index = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||||
|
int nextIndex = index + 1;
|
||||||
|
mFrequencyModel.add(DEFAULT_FREQUENCY, nextIndex);
|
||||||
|
mFrequencyTable.requestFocus();
|
||||||
|
mFrequencyTable.setRowSelectionInterval(nextIndex, nextIndex);
|
||||||
|
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(nextIndex, 0, true)));
|
||||||
|
mFrequencyTable.editCellAt(nextIndex, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mFrequencyModel.add(DEFAULT_FREQUENCY);
|
||||||
|
int lastIndex = mFrequencyModel.getRowCount() - 1;
|
||||||
|
mFrequencyTable.setRowSelectionInterval(lastIndex, lastIndex);
|
||||||
|
mFrequencyTable.editCellAt(lastIndex, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
mFrequencyTable.setSurrendersFocusOnKeystroke(true);
|
||||||
|
mFrequencyTable.getEditorComponent().requestFocus();
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
add(mAddButton, "wrap");
|
||||||
|
|
||||||
|
mMoveDownButton = new JButton();
|
||||||
|
mMoveDownButton.setIcon(FontIcon.of(FontAwesome.ARROW_DOWN, 10));
|
||||||
|
mMoveDownButton.setEnabled(false);
|
||||||
|
mMoveDownButton.addActionListener(e -> {
|
||||||
|
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||||
|
int index = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||||
|
mFrequencyModel.moveDown(index);
|
||||||
|
mFrequencyTable.getSelectionModel().setLeadSelectionIndex(index + 1);
|
||||||
|
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(selectedRow, 0, true)));
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
add(mMoveDownButton);
|
||||||
|
|
||||||
|
mRemoveButton = new JButton();
|
||||||
|
mRemoveButton.setIcon(FontIcon.of(FontAwesome.MINUS, 10));
|
||||||
|
mRemoveButton.setEnabled(false);
|
||||||
|
mRemoveButton.addActionListener(e -> {
|
||||||
|
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||||
|
int selectedIndex = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||||
|
mFrequencyModel.remove(selectedIndex);
|
||||||
|
|
||||||
|
if(selectedIndex < mFrequencyModel.getRowCount())
|
||||||
|
{
|
||||||
|
int reselectedRow = mFrequencyTable.convertRowIndexToView(selectedIndex);
|
||||||
|
mFrequencyTable.setRowSelectionInterval(reselectedRow, reselectedRow);
|
||||||
|
mFrequencyTable.scrollRectToVisible(new Rectangle(mFrequencyTable.getCellRect(reselectedRow, 0, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateButtons();
|
||||||
|
});
|
||||||
|
add(mRemoveButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrequencyModel getFrequencyModel()
|
||||||
|
{
|
||||||
|
return mFrequencyModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateButtons()
|
||||||
|
{
|
||||||
|
int index = mFrequencyTable.getSelectedRow();
|
||||||
|
|
||||||
|
if(index >= 0)
|
||||||
|
{
|
||||||
|
mMoveUpButton.setEnabled(index > 0);
|
||||||
|
mMoveDownButton.setEnabled(index < mFrequencyModel.getRowCount() - 1);
|
||||||
|
mRemoveButton.setEnabled(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRemoveButton.setEnabled(false);
|
||||||
|
mMoveUpButton.setEnabled(false);
|
||||||
|
mMoveDownButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Formats the frequency value as MHz
|
||||||
|
*/
|
||||||
|
private String format(long frequency)
|
||||||
|
{
|
||||||
|
return mDecimalFormat.format(frequency / 1e6d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FrequencyModel extends AbstractTableModel
|
||||||
|
{
|
||||||
|
private List<Long> mFrequencies = new ArrayList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int column)
|
||||||
|
{
|
||||||
|
return "Frequencies (MHz)";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount()
|
||||||
|
{
|
||||||
|
return mFrequencies.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int rowIndex, int columnIndex)
|
||||||
|
{
|
||||||
|
return mFrequencies.get(rowIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValueAt(int index, Long value)
|
||||||
|
{
|
||||||
|
mFrequencies.set(index, value);
|
||||||
|
fireTableRowsUpdated(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCellEditable(int rowIndex, int columnIndex)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getColumnClass(int columnIndex)
|
||||||
|
{
|
||||||
|
return Long.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a frequency to the model
|
||||||
|
*/
|
||||||
|
public void add(long frequency, int index)
|
||||||
|
{
|
||||||
|
mFrequencies.add(index, frequency);
|
||||||
|
fireTableRowsInserted(index, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a frequency to the model
|
||||||
|
*/
|
||||||
|
public void add(long frequency)
|
||||||
|
{
|
||||||
|
mFrequencies.add(frequency);
|
||||||
|
fireTableRowsInserted(mFrequencies.size() - 1, mFrequencies.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes an indexed value from the model
|
||||||
|
*/
|
||||||
|
public Long remove(int index)
|
||||||
|
{
|
||||||
|
Long value = mFrequencies.remove(index);
|
||||||
|
fireTableRowsDeleted(index, index);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all frequencies from the model
|
||||||
|
*/
|
||||||
|
public void clear()
|
||||||
|
{
|
||||||
|
mFrequencies.clear();
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the frequency up in the list
|
||||||
|
*/
|
||||||
|
public void moveUp(int index)
|
||||||
|
{
|
||||||
|
if(index > 0 && index < mFrequencies.size())
|
||||||
|
{
|
||||||
|
Long valueA = mFrequencies.get(index);
|
||||||
|
Long valueB = mFrequencies.get(index - 1);
|
||||||
|
mFrequencies.set(index, valueB);
|
||||||
|
mFrequencies.set(index - 1, valueA);
|
||||||
|
|
||||||
|
fireTableRowsUpdated(index - 1, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the frequency down in the list
|
||||||
|
*/
|
||||||
|
public void moveDown(int index)
|
||||||
|
{
|
||||||
|
if(index >= 0 && index + 1 < mFrequencies.size())
|
||||||
|
{
|
||||||
|
Long valueA = mFrequencies.get(index);
|
||||||
|
Long valueB = mFrequencies.get(index + 1);
|
||||||
|
mFrequencies.set(index, valueB);
|
||||||
|
mFrequencies.set(index + 1, valueA);
|
||||||
|
|
||||||
|
fireTableRowsUpdated(index, index + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the frequencies for the model
|
||||||
|
*/
|
||||||
|
public void setFrequencies(List<Long> frequencies)
|
||||||
|
{
|
||||||
|
mFrequencies.clear();
|
||||||
|
mFrequencies.addAll(frequencies);
|
||||||
|
fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of long frequency values contained in the model
|
||||||
|
*/
|
||||||
|
public List<Long> getFrequencies()
|
||||||
|
{
|
||||||
|
List<Long> frequencies = new ArrayList<>();
|
||||||
|
frequencies.addAll(mFrequencies);
|
||||||
|
return frequencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SelectionListener implements ListSelectionListener
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void valueChanged(ListSelectionEvent event)
|
||||||
|
{
|
||||||
|
updateButtons();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FrequencyEditor extends DefaultCellEditor
|
||||||
|
{
|
||||||
|
private JFormattedTextField mTextField;
|
||||||
|
private NumberFormat mNumberFormat;
|
||||||
|
private Double mMinimum;
|
||||||
|
private Double mMaximum;
|
||||||
|
|
||||||
|
public FrequencyEditor(double minValue, double maxValue)
|
||||||
|
{
|
||||||
|
super(new JFormattedTextField());
|
||||||
|
mTextField = (JFormattedTextField)getComponent();
|
||||||
|
mMinimum = minValue;
|
||||||
|
mMaximum = maxValue;
|
||||||
|
|
||||||
|
mNumberFormat = NumberFormat.getNumberInstance();
|
||||||
|
mNumberFormat.setMinimumFractionDigits(1);
|
||||||
|
mNumberFormat.setMaximumFractionDigits(6);
|
||||||
|
mNumberFormat.setGroupingUsed(false);
|
||||||
|
NumberFormatter numberFormatter = new NumberFormatter(mNumberFormat);
|
||||||
|
numberFormatter.setFormat(mNumberFormat);
|
||||||
|
numberFormatter.setMinimum(mMinimum);
|
||||||
|
numberFormatter.setMaximum(mMaximum);
|
||||||
|
numberFormatter.setAllowsInvalid(false);
|
||||||
|
|
||||||
|
mTextField.setFormatterFactory(new DefaultFormatterFactory(numberFormatter));
|
||||||
|
mTextField.setValue(mMinimum);
|
||||||
|
mTextField.setHorizontalAlignment(JTextField.TRAILING);
|
||||||
|
mTextField.setFocusLostBehavior(JFormattedTextField.PERSIST);
|
||||||
|
mTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "check");
|
||||||
|
mTextField.getActionMap().put("check", new AbstractAction()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent event)
|
||||||
|
{
|
||||||
|
//This should always be true since we use formatter.setAllowsInvalid(false) above.
|
||||||
|
if(mTextField.isEditValid())
|
||||||
|
{
|
||||||
|
int selectedRow = mFrequencyTable.getSelectedRow();
|
||||||
|
int selectedIndex = mFrequencyTable.convertRowIndexToModel(selectedRow);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Double frequencyMHz = Double.parseDouble(mTextField.getText());
|
||||||
|
|
||||||
|
long frequency = (long)(frequencyMHz * 1e6d);
|
||||||
|
mFrequencyModel.setValueAt(selectedIndex, frequency);
|
||||||
|
}
|
||||||
|
catch(Exception err)
|
||||||
|
{
|
||||||
|
mLog.error("Error parsing frequency: " + mTextField.getText(), err);
|
||||||
|
}
|
||||||
|
|
||||||
|
fireEditingStopped();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//Override to invoke setValue on the formatted text field.
|
||||||
|
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column)
|
||||||
|
{
|
||||||
|
JFormattedTextField ftf = (JFormattedTextField)super.getTableCellEditorComponent(table, value, isSelected, row, column);
|
||||||
|
|
||||||
|
if(value instanceof Long)
|
||||||
|
{
|
||||||
|
ftf.setValue(((Long)value).doubleValue() / 1e6d);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ftf.setValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
ftf.requestFocus();
|
||||||
|
ftf.selectAll();
|
||||||
|
ftf.setCaretPosition(0);
|
||||||
|
return ftf;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Override to ensure that the value remains a Double.
|
||||||
|
public Object getCellEditorValue()
|
||||||
|
{
|
||||||
|
JFormattedTextField ftf = (JFormattedTextField)getComponent();
|
||||||
|
Object o = ftf.getValue();
|
||||||
|
if(o instanceof Double)
|
||||||
|
{
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
else if(o instanceof Number)
|
||||||
|
{
|
||||||
|
return ((Number)o).doubleValue();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Double.parseDouble(o.toString());
|
||||||
|
}
|
||||||
|
catch(Exception exc)
|
||||||
|
{
|
||||||
|
return new Double(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Table cell renderer to display long hertz values as double megahertz values
|
||||||
|
*/
|
||||||
|
public class FrequencyRenderer extends DefaultTableCellRenderer
|
||||||
|
{
|
||||||
|
|
||||||
|
public FrequencyRenderer()
|
||||||
|
{
|
||||||
|
super.setHorizontalAlignment(JLabel.RIGHT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
|
||||||
|
{
|
||||||
|
JLabel cell = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
|
||||||
|
|
||||||
|
if(value instanceof Long)
|
||||||
|
{
|
||||||
|
cell.setText(format((long)value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cell.setText("??");
|
||||||
|
}
|
||||||
|
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args)
|
||||||
|
{
|
||||||
|
EventQueue.invokeLater(() -> {
|
||||||
|
final JFrame frame = new JFrame();
|
||||||
|
frame.setSize(new Dimension(400, 400));
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
frame.add(new MultipleFrequencyEditor(1.0, 6000.0));
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
frame.setVisible(true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,6 +21,7 @@
|
|||||||
package io.github.dsheirer.gui.preference;
|
package io.github.dsheirer.gui.preference;
|
||||||
|
|
||||||
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
|
import io.github.dsheirer.gui.preference.directory.DirectoryPreferenceEditor;
|
||||||
|
import io.github.dsheirer.gui.preference.tuner.ChannelMultipleFrequencyPreferenceEditor;
|
||||||
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
|
import io.github.dsheirer.gui.preference.tuner.TunerPreferenceEditor;
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import javafx.scene.Node;
|
import javafx.scene.Node;
|
||||||
@@ -38,9 +39,11 @@ public class PreferenceEditorFactory
|
|||||||
return new DecodeEventViewPreferenceEditor(userPreferences);
|
return new DecodeEventViewPreferenceEditor(userPreferences);
|
||||||
case DIRECTORY:
|
case DIRECTORY:
|
||||||
return new DirectoryPreferenceEditor(userPreferences);
|
return new DirectoryPreferenceEditor(userPreferences);
|
||||||
|
case SOURCE_CHANNEL_MULTIPLE_FREQUENCY:
|
||||||
|
return new ChannelMultipleFrequencyPreferenceEditor(userPreferences);
|
||||||
case TALKGROUP_FORMAT:
|
case TALKGROUP_FORMAT:
|
||||||
return new TalkgroupFormatPreferenceEditor(userPreferences);
|
return new TalkgroupFormatPreferenceEditor(userPreferences);
|
||||||
case TUNER:
|
case SOURCE_TUNER_CHANNELIZER:
|
||||||
return new TunerPreferenceEditor(userPreferences);
|
return new TunerPreferenceEditor(userPreferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,8 +27,9 @@ public enum PreferenceEditorType
|
|||||||
{
|
{
|
||||||
CHANNEL_EVENT("Channel Events"),
|
CHANNEL_EVENT("Channel Events"),
|
||||||
DIRECTORY("Directories"),
|
DIRECTORY("Directories"),
|
||||||
TALKGROUP_FORMAT("Talkgroups"),
|
SOURCE_CHANNEL_MULTIPLE_FREQUENCY("Channel - Multiple Frequency"),
|
||||||
TUNER("Tuners");
|
SOURCE_TUNER_CHANNELIZER("Tuner Channelizer"),
|
||||||
|
TALKGROUP_FORMAT("Talkgroups");
|
||||||
|
|
||||||
private String mLabel;
|
private String mLabel;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -201,7 +201,8 @@ public class PreferencesEditor extends Application
|
|||||||
storageItem.setExpanded(true);
|
storageItem.setExpanded(true);
|
||||||
|
|
||||||
TreeItem<String> sourceItem = new TreeItem<>("Source");
|
TreeItem<String> sourceItem = new TreeItem<>("Source");
|
||||||
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.TUNER));
|
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_CHANNEL_MULTIPLE_FREQUENCY));
|
||||||
|
sourceItem.getChildren().add(new TreeItem(PreferenceEditorType.SOURCE_TUNER_CHANNELIZER));
|
||||||
treeRoot.getChildren().add(sourceItem);
|
treeRoot.getChildren().add(sourceItem);
|
||||||
sourceItem.setExpanded(true);
|
sourceItem.setExpanded(true);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 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.gui.preference.tuner;
|
||||||
|
|
||||||
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
|
import io.github.dsheirer.preference.source.ChannelMultiFrequencyPreference;
|
||||||
|
import javafx.geometry.Insets;
|
||||||
|
import javafx.scene.control.Button;
|
||||||
|
import javafx.scene.control.Label;
|
||||||
|
import javafx.scene.control.Spinner;
|
||||||
|
import javafx.scene.layout.GridPane;
|
||||||
|
import javafx.scene.layout.HBox;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preference settings for channel source configurations containing multiple frequencies
|
||||||
|
*/
|
||||||
|
public class ChannelMultipleFrequencyPreferenceEditor extends HBox
|
||||||
|
{
|
||||||
|
private static final String HELP_TEXT = "Rotation delay determines how long a decoder will wait before rotating " +
|
||||||
|
"to the next frequency in a list for a rotating control channel configuration.";
|
||||||
|
|
||||||
|
private static final Insets MARGINS = new Insets(5,5,5,5);
|
||||||
|
private ChannelMultiFrequencyPreference mChannelMultiFrequencyPreference;
|
||||||
|
private GridPane mEditorPane;
|
||||||
|
private Label mRotationDelayLabel;
|
||||||
|
private Spinner<Integer> mRotationDelaySpinner;
|
||||||
|
private Button mResetButton;
|
||||||
|
private Label mHelpText;
|
||||||
|
|
||||||
|
public ChannelMultipleFrequencyPreferenceEditor(UserPreferences userPreferences)
|
||||||
|
{
|
||||||
|
mChannelMultiFrequencyPreference = userPreferences.getChannelMultiFrequencyPreference();
|
||||||
|
getChildren().add(getEditorPane());
|
||||||
|
}
|
||||||
|
|
||||||
|
private GridPane getEditorPane()
|
||||||
|
{
|
||||||
|
if(mEditorPane == null)
|
||||||
|
{
|
||||||
|
mEditorPane = new GridPane();
|
||||||
|
GridPane.setMargin(getRotationDelaySpinner(), new Insets(5, 5, 5, 5));
|
||||||
|
mEditorPane.add(getRotationDelaySpinner(), 0, 0);
|
||||||
|
GridPane.setMargin(getRotationDelayLabel(), new Insets(5, 5, 5, 5));
|
||||||
|
mEditorPane.add(getRotationDelayLabel(), 1, 0);
|
||||||
|
GridPane.setMargin(getResetButton(), MARGINS);
|
||||||
|
mEditorPane.add(getResetButton(), 2, 0);
|
||||||
|
GridPane.setMargin(getHelpText(), MARGINS);
|
||||||
|
mEditorPane.add(getHelpText(), 0, 1, 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mEditorPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Spinner<Integer> getRotationDelaySpinner()
|
||||||
|
{
|
||||||
|
if(mRotationDelaySpinner == null)
|
||||||
|
{
|
||||||
|
int currentDelay = (int)(mChannelMultiFrequencyPreference.getRotationDelay() / 1000);
|
||||||
|
mRotationDelaySpinner = new Spinner<>(1, 60, currentDelay, 1);
|
||||||
|
mRotationDelaySpinner.setEditable(true);
|
||||||
|
mRotationDelaySpinner.valueProperty().addListener((observable, oldValue, newValue) -> {
|
||||||
|
mChannelMultiFrequencyPreference.setRotationDelay(newValue * 1000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mRotationDelaySpinner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Label getRotationDelayLabel()
|
||||||
|
{
|
||||||
|
if(mRotationDelayLabel == null)
|
||||||
|
{
|
||||||
|
mRotationDelayLabel = new Label("Rotation Delay (seconds)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return mRotationDelayLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Button getResetButton()
|
||||||
|
{
|
||||||
|
if(mResetButton == null)
|
||||||
|
{
|
||||||
|
mResetButton = new Button("Reset");
|
||||||
|
mResetButton.setOnAction(event -> {
|
||||||
|
mChannelMultiFrequencyPreference.resetRotationDelay();
|
||||||
|
int currentDelay = (int)(mChannelMultiFrequencyPreference.getRotationDelay() / 1000);
|
||||||
|
getRotationDelaySpinner().getValueFactory().setValue(currentDelay);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mResetButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Label getHelpText()
|
||||||
|
{
|
||||||
|
if(mHelpText == null)
|
||||||
|
{
|
||||||
|
mHelpText = new Label(HELP_TEXT);
|
||||||
|
mHelpText.setWrapText(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mHelpText;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -21,8 +21,8 @@
|
|||||||
package io.github.dsheirer.gui.preference.tuner;
|
package io.github.dsheirer.gui.preference.tuner;
|
||||||
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.preference.tuner.ChannelizerType;
|
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||||
import io.github.dsheirer.preference.tuner.TunerPreference;
|
import io.github.dsheirer.preference.source.TunerPreference;
|
||||||
import javafx.geometry.HPos;
|
import javafx.geometry.HPos;
|
||||||
import javafx.geometry.Insets;
|
import javafx.geometry.Insets;
|
||||||
import javafx.geometry.Orientation;
|
import javafx.geometry.Orientation;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -48,12 +48,12 @@ public class IdentifierUpdateNotification
|
|||||||
return mOperation;
|
return mOperation;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isAdded()
|
public boolean isAdd()
|
||||||
{
|
{
|
||||||
return mOperation == Operation.ADD;
|
return mOperation == Operation.ADD;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isRemoved()
|
public boolean isRemove()
|
||||||
{
|
{
|
||||||
return mOperation == Operation.REMOVE;
|
return mOperation == Operation.REMOVE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -367,11 +367,11 @@ public class MutableIdentifierCollection extends IdentifierCollection implements
|
|||||||
@Override
|
@Override
|
||||||
public void receive(IdentifierUpdateNotification identifierUpdateNotification)
|
public void receive(IdentifierUpdateNotification identifierUpdateNotification)
|
||||||
{
|
{
|
||||||
if(identifierUpdateNotification.isAdded())
|
if(identifierUpdateNotification.isAdd())
|
||||||
{
|
{
|
||||||
update(identifierUpdateNotification.getIdentifier());
|
update(identifierUpdateNotification.getIdentifier());
|
||||||
}
|
}
|
||||||
else if(identifierUpdateNotification.isRemoved())
|
else if(identifierUpdateNotification.isRemove())
|
||||||
{
|
{
|
||||||
remove(identifierUpdateNotification.getIdentifier());
|
remove(identifierUpdateNotification.getIdentifier());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -27,19 +27,19 @@ import io.github.dsheirer.identifier.IdentifierClass;
|
|||||||
import io.github.dsheirer.identifier.Role;
|
import io.github.dsheirer.identifier.Role;
|
||||||
import io.github.dsheirer.protocol.Protocol;
|
import io.github.dsheirer.protocol.Protocol;
|
||||||
|
|
||||||
public class DecoderStateIdentifier extends Identifier<State>
|
public class ChannelStateIdentifier extends Identifier<State>
|
||||||
{
|
{
|
||||||
public static final DecoderStateIdentifier ACTIVE = DecoderStateIdentifier.create(State.ACTIVE);
|
public static final ChannelStateIdentifier ACTIVE = ChannelStateIdentifier.create(State.ACTIVE);
|
||||||
public static final DecoderStateIdentifier CALL = DecoderStateIdentifier.create(State.CALL);
|
public static final ChannelStateIdentifier CALL = ChannelStateIdentifier.create(State.CALL);
|
||||||
public static final DecoderStateIdentifier CONTROL = DecoderStateIdentifier.create(State.CONTROL);
|
public static final ChannelStateIdentifier CONTROL = ChannelStateIdentifier.create(State.CONTROL);
|
||||||
public static final DecoderStateIdentifier DATA = DecoderStateIdentifier.create(State.DATA);
|
public static final ChannelStateIdentifier DATA = ChannelStateIdentifier.create(State.DATA);
|
||||||
public static final DecoderStateIdentifier ENCRYPTED = DecoderStateIdentifier.create(State.ENCRYPTED);
|
public static final ChannelStateIdentifier ENCRYPTED = ChannelStateIdentifier.create(State.ENCRYPTED);
|
||||||
public static final DecoderStateIdentifier FADE = DecoderStateIdentifier.create(State.FADE);
|
public static final ChannelStateIdentifier FADE = ChannelStateIdentifier.create(State.FADE);
|
||||||
public static final DecoderStateIdentifier IDLE = DecoderStateIdentifier.create(State.IDLE);
|
public static final ChannelStateIdentifier IDLE = ChannelStateIdentifier.create(State.IDLE);
|
||||||
public static final DecoderStateIdentifier TEARDOWN = DecoderStateIdentifier.create(State.TEARDOWN);
|
public static final ChannelStateIdentifier TEARDOWN = ChannelStateIdentifier.create(State.TEARDOWN);
|
||||||
public static final DecoderStateIdentifier RESET = DecoderStateIdentifier.create(State.RESET);
|
public static final ChannelStateIdentifier RESET = ChannelStateIdentifier.create(State.RESET);
|
||||||
|
|
||||||
public DecoderStateIdentifier(State value)
|
public ChannelStateIdentifier(State value)
|
||||||
{
|
{
|
||||||
super(value, IdentifierClass.DECODER, Form.STATE, Role.ANY);
|
super(value, IdentifierClass.DECODER, Form.STATE, Role.ANY);
|
||||||
}
|
}
|
||||||
@@ -53,9 +53,9 @@ public class DecoderStateIdentifier extends Identifier<State>
|
|||||||
/**
|
/**
|
||||||
* Creates a decoder state identifier from the decoder state value
|
* Creates a decoder state identifier from the decoder state value
|
||||||
*/
|
*/
|
||||||
public static DecoderStateIdentifier create(State state)
|
public static ChannelStateIdentifier create(State state)
|
||||||
{
|
{
|
||||||
return new DecoderStateIdentifier(state);
|
return new ChannelStateIdentifier(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -456,7 +456,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
|||||||
|
|
||||||
if(module instanceof IHeartbeatProvider)
|
if(module instanceof IHeartbeatProvider)
|
||||||
{
|
{
|
||||||
((IHeartbeatProvider)module).getHeartbeatManager().addHeartbeatListener(mHeartbeatBroadcaster);
|
((IHeartbeatProvider)module).addHeartbeatListener(mHeartbeatBroadcaster);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(module instanceof IMessageProvider)
|
if(module instanceof IMessageProvider)
|
||||||
@@ -528,7 +528,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
|||||||
|
|
||||||
if(module instanceof IHeartbeatProvider)
|
if(module instanceof IHeartbeatProvider)
|
||||||
{
|
{
|
||||||
((IHeartbeatProvider)module).getHeartbeatManager().removeHeartbeatListener(mHeartbeatBroadcaster);
|
((IHeartbeatProvider)module).removeHeartbeatListener(mHeartbeatBroadcaster);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(module instanceof IMessageProvider)
|
if(module instanceof IMessageProvider)
|
||||||
@@ -571,7 +571,7 @@ public class ProcessingChain implements Listener<ChannelEvent>
|
|||||||
mSourceEventBroadcaster.broadcast(SourceEvent.sampleRateChange(mSource.getSampleRate(), "Processing Chain Startup"));
|
mSourceEventBroadcaster.broadcast(SourceEvent.sampleRateChange(mSource.getSampleRate(), "Processing Chain Startup"));
|
||||||
|
|
||||||
//Broadcast the source center frequency / sample rate so that each of the modules can self-configure
|
//Broadcast the source center frequency / sample rate so that each of the modules can self-configure
|
||||||
mSourceEventBroadcaster.broadcast(SourceEvent.frequencyChange(mSource.getFrequency(), "Processing Chain Startup"));
|
mSourceEventBroadcaster.broadcast(SourceEvent.frequencyChange(mSource, mSource.getFrequency(), "Processing Chain Startup"));
|
||||||
|
|
||||||
//Setup the channel state to monitor source overflow conditions
|
//Setup the channel state to monitor source overflow conditions
|
||||||
mSource.setOverflowListener(mChannelState);
|
mSource.setOverflowListener(mChannelState);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -24,6 +24,7 @@ import io.github.dsheirer.alias.AliasModel;
|
|||||||
import io.github.dsheirer.alias.action.AliasActionManager;
|
import io.github.dsheirer.alias.action.AliasActionManager;
|
||||||
import io.github.dsheirer.audio.AudioModule;
|
import io.github.dsheirer.audio.AudioModule;
|
||||||
import io.github.dsheirer.channel.state.AlwaysUnsquelchedDecoderState;
|
import io.github.dsheirer.channel.state.AlwaysUnsquelchedDecoderState;
|
||||||
|
import io.github.dsheirer.channel.state.State;
|
||||||
import io.github.dsheirer.controller.channel.Channel;
|
import io.github.dsheirer.controller.channel.Channel;
|
||||||
import io.github.dsheirer.controller.channel.Channel.ChannelType;
|
import io.github.dsheirer.controller.channel.Channel.ChannelType;
|
||||||
import io.github.dsheirer.controller.channel.map.ChannelMap;
|
import io.github.dsheirer.controller.channel.map.ChannelMap;
|
||||||
@@ -87,7 +88,10 @@ import io.github.dsheirer.module.decode.tait.Tait1200Decoder;
|
|||||||
import io.github.dsheirer.module.decode.tait.Tait1200DecoderState;
|
import io.github.dsheirer.module.decode.tait.Tait1200DecoderState;
|
||||||
import io.github.dsheirer.module.demodulate.am.AMDemodulatorModule;
|
import io.github.dsheirer.module.demodulate.am.AMDemodulatorModule;
|
||||||
import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule;
|
import io.github.dsheirer.module.demodulate.fm.FMDemodulatorModule;
|
||||||
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.source.SourceType;
|
import io.github.dsheirer.source.SourceType;
|
||||||
|
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
|
||||||
|
import io.github.dsheirer.source.tuner.channel.ChannelRotationMonitor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@@ -109,9 +113,10 @@ public class DecoderFactory
|
|||||||
*
|
*
|
||||||
* @return list of configured decoders
|
* @return list of configured decoders
|
||||||
*/
|
*/
|
||||||
public static List<Module> getModules(ChannelMapModel channelMapModel, Channel channel, AliasModel aliasModel)
|
public static List<Module> getModules(ChannelMapModel channelMapModel, Channel channel, AliasModel aliasModel,
|
||||||
|
UserPreferences userPreferences)
|
||||||
{
|
{
|
||||||
List<Module> modules = getPrimaryModules(channelMapModel, channel, aliasModel);
|
List<Module> modules = getPrimaryModules(channelMapModel, channel, aliasModel, userPreferences);
|
||||||
modules.addAll(getAuxiliaryDecoders(channel.getAuxDecodeConfiguration()));
|
modules.addAll(getAuxiliaryDecoders(channel.getAuxDecodeConfiguration()));
|
||||||
return modules;
|
return modules;
|
||||||
}
|
}
|
||||||
@@ -119,7 +124,8 @@ public class DecoderFactory
|
|||||||
/**
|
/**
|
||||||
* Constructs a primary decoder as specified in the decode configuration
|
* Constructs a primary decoder as specified in the decode configuration
|
||||||
*/
|
*/
|
||||||
public static List<Module> getPrimaryModules(ChannelMapModel channelMapModel, Channel channel, AliasModel aliasModel)
|
public static List<Module> getPrimaryModules(ChannelMapModel channelMapModel, Channel channel,
|
||||||
|
AliasModel aliasModel, UserPreferences userPreferences)
|
||||||
{
|
{
|
||||||
List<Module> modules = new ArrayList<Module>();
|
List<Module> modules = new ArrayList<Module>();
|
||||||
|
|
||||||
@@ -239,6 +245,15 @@ public class DecoderFactory
|
|||||||
}
|
}
|
||||||
|
|
||||||
modules.add(new P25AudioModule());
|
modules.add(new P25AudioModule());
|
||||||
|
|
||||||
|
//Add a channel rotation monitor when we have multiple control channel frequencies specified
|
||||||
|
if(channel.getSourceConfiguration() instanceof SourceConfigTunerMultipleFrequency &&
|
||||||
|
((SourceConfigTunerMultipleFrequency)channel.getSourceConfiguration()).hasMultipleFrequencies())
|
||||||
|
{
|
||||||
|
List<State> activeStates = new ArrayList<>();
|
||||||
|
activeStates.add(State.CONTROL);
|
||||||
|
modules.add(new ChannelRotationMonitor(activeStates, userPreferences));
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException("Unknown decoder type [" + decodeConfig.getDecoderType().toString() + "]");
|
throw new IllegalArgumentException("Unknown decoder type [" + decodeConfig.getDecoderType().toString() + "]");
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* SDR Trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
package io.github.dsheirer.module.decode.p25;
|
package io.github.dsheirer.module.decode.p25;
|
||||||
|
|
||||||
import io.github.dsheirer.controller.channel.Channel;
|
import io.github.dsheirer.controller.channel.Channel;
|
||||||
@@ -26,6 +28,7 @@ import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
|
|||||||
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
import io.github.dsheirer.module.decode.config.DecodeConfiguration;
|
||||||
import io.github.dsheirer.source.SourceConfigurationEditor;
|
import io.github.dsheirer.source.SourceConfigurationEditor;
|
||||||
import io.github.dsheirer.source.config.SourceConfigTuner;
|
import io.github.dsheirer.source.config.SourceConfigTuner;
|
||||||
|
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
|
||||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@@ -43,9 +46,9 @@ import java.awt.event.ActionListener;
|
|||||||
|
|
||||||
public class P25DecoderEditor extends ValidatingEditor<Channel>
|
public class P25DecoderEditor extends ValidatingEditor<Channel>
|
||||||
{
|
{
|
||||||
private final static Logger mLog = LoggerFactory.getLogger( P25DecoderEditor.class );
|
private final static Logger mLog = LoggerFactory.getLogger(P25DecoderEditor.class);
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private JComboBox<P25DecoderLSM.Modulation> mComboModulation;
|
private JComboBox<P25DecoderLSM.Modulation> mComboModulation;
|
||||||
private JCheckBox mIgnoreDataCalls;
|
private JCheckBox mIgnoreDataCalls;
|
||||||
@@ -53,168 +56,166 @@ public class P25DecoderEditor extends ValidatingEditor<Channel>
|
|||||||
private JSlider mTrafficChannelPoolSize;
|
private JSlider mTrafficChannelPoolSize;
|
||||||
|
|
||||||
public P25DecoderEditor()
|
public P25DecoderEditor()
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
}
|
|
||||||
|
|
||||||
private void init()
|
|
||||||
{
|
|
||||||
setLayout( new MigLayout( "insets 0 0 0 0,wrap 3", "[right][grow,fill][]", "" ) );
|
|
||||||
|
|
||||||
mComboModulation = new JComboBox<P25DecoderLSM.Modulation>();
|
|
||||||
mComboModulation.setModel( new DefaultComboBoxModel<P25DecoderLSM.Modulation>(
|
|
||||||
P25DecoderLSM.Modulation.values() ) );
|
|
||||||
mComboModulation.setEnabled( false );
|
|
||||||
mComboModulation.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( new JLabel( "Modulation:" ) );
|
|
||||||
add( mComboModulation );
|
|
||||||
|
|
||||||
mIgnoreDataCalls = new JCheckBox( "Ignore Data Calls" );
|
|
||||||
mIgnoreDataCalls.setEnabled( false );
|
|
||||||
mIgnoreDataCalls.addActionListener( new ActionListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void actionPerformed( ActionEvent e )
|
|
||||||
{
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
add( mIgnoreDataCalls );
|
|
||||||
|
|
||||||
mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM,
|
|
||||||
DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT );
|
|
||||||
mTrafficChannelPoolSize.setEnabled( false );
|
|
||||||
mTrafficChannelPoolSize.setMajorTickSpacing( 10 );
|
|
||||||
mTrafficChannelPoolSize.setMinorTickSpacing( 5 );
|
|
||||||
mTrafficChannelPoolSize.setPaintTicks( true );
|
|
||||||
mTrafficChannelPoolSize.addChangeListener( new ChangeListener()
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void stateChanged( ChangeEvent e )
|
|
||||||
{
|
|
||||||
mTrafficChannelPoolSizeLabel.setText( "Traffic Pool: " + mTrafficChannelPoolSize.getValue() );
|
|
||||||
setModified( true );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
|
|
||||||
mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Pool: " + mTrafficChannelPoolSize.getValue() + " " );
|
|
||||||
|
|
||||||
add( mTrafficChannelPoolSizeLabel );
|
|
||||||
add( mTrafficChannelPoolSize );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void save()
|
|
||||||
{
|
|
||||||
if( hasItem() && isModified() )
|
|
||||||
{
|
|
||||||
DecodeConfigP25Phase1 p25 = new DecodeConfigP25Phase1();
|
|
||||||
|
|
||||||
p25.setModulation( (P25Decoder.Modulation)mComboModulation.getSelectedItem() );
|
|
||||||
p25.setIgnoreDataCalls( mIgnoreDataCalls.isSelected() );
|
|
||||||
p25.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() );
|
|
||||||
|
|
||||||
getItem().setDecodeConfiguration( p25 );
|
|
||||||
}
|
|
||||||
|
|
||||||
setModified( false );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void init()
|
||||||
public void validate( Editor<Channel> editor ) throws EditorValidationException
|
{
|
||||||
{
|
setLayout(new MigLayout("insets 0 0 0 0,wrap 3", "[right][grow,fill][]", ""));
|
||||||
/**
|
|
||||||
* Enforce CQPSK modulation uses a Tuner Source for I/Q sample data
|
|
||||||
*/
|
|
||||||
if( editor instanceof SourceConfigurationEditor &&
|
|
||||||
((P25Decoder.Modulation)mComboModulation.getSelectedItem()) == P25Decoder.Modulation.CQPSK )
|
|
||||||
{
|
|
||||||
SourceConfiguration config = ((SourceConfigurationEditor)editor).getSourceConfiguration();
|
|
||||||
|
|
||||||
if( config != null && !( config instanceof SourceConfigTuner ) )
|
mComboModulation = new JComboBox<P25DecoderLSM.Modulation>();
|
||||||
{
|
mComboModulation.setModel(new DefaultComboBoxModel<P25DecoderLSM.Modulation>(
|
||||||
throw new EditorValidationException( editor,
|
P25DecoderLSM.Modulation.values()));
|
||||||
"<html><body width='175'><h3>LSM Simulcast</h3>"
|
mComboModulation.setEnabled(false);
|
||||||
+ "<p>P25 LSM Simulcast decoder can only be used with "
|
mComboModulation.addActionListener(new ActionListener()
|
||||||
+ "a tuner source. Please change the Source to use a tuner"
|
{
|
||||||
+ " or change the P25 Decoder to C4FM modulation</p>" );
|
@Override
|
||||||
}
|
public void actionPerformed(ActionEvent e)
|
||||||
}
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if( editor instanceof AuxDecodeConfigurationEditor)
|
add(new JLabel("Modulation:"));
|
||||||
{
|
add(mComboModulation);
|
||||||
AuxDecodeConfiguration config = ((AuxDecodeConfigurationEditor)editor).getConfiguration();
|
|
||||||
|
|
||||||
if( config != null && !config.getAuxDecoders().isEmpty() )
|
mIgnoreDataCalls = new JCheckBox("Ignore Data Calls");
|
||||||
{
|
mIgnoreDataCalls.setEnabled(false);
|
||||||
throw new EditorValidationException( editor,
|
mIgnoreDataCalls.addActionListener(new ActionListener()
|
||||||
"<html><body width='175'><h3>Auxilary Decoders</h3>"
|
{
|
||||||
+ "<p>The auxiliary decoders work with analog audio and are not "
|
@Override
|
||||||
+ "compatible with the P25 Decoder. Please uncheck any auxiliary "
|
public void actionPerformed(ActionEvent e)
|
||||||
+ "decoders that you have selected for this channel</p>" );
|
{
|
||||||
}
|
setModified(true);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
private void setControlsEnabled( boolean enabled )
|
add(mIgnoreDataCalls);
|
||||||
{
|
|
||||||
if( mComboModulation.isEnabled() != enabled )
|
|
||||||
{
|
|
||||||
mComboModulation.setEnabled( enabled );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( mTrafficChannelPoolSize.isEnabled() != enabled )
|
mTrafficChannelPoolSize = new JSlider(JSlider.HORIZONTAL, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM,
|
||||||
{
|
DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT);
|
||||||
mTrafficChannelPoolSize.setEnabled( enabled );
|
mTrafficChannelPoolSize.setEnabled(false);
|
||||||
}
|
mTrafficChannelPoolSize.setMajorTickSpacing(10);
|
||||||
|
mTrafficChannelPoolSize.setMinorTickSpacing(5);
|
||||||
|
mTrafficChannelPoolSize.setPaintTicks(true);
|
||||||
|
mTrafficChannelPoolSize.addChangeListener(new ChangeListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e)
|
||||||
|
{
|
||||||
|
mTrafficChannelPoolSizeLabel.setText("Traffic Pool: " + mTrafficChannelPoolSize.getValue());
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if( mIgnoreDataCalls.isEnabled() != enabled )
|
mTrafficChannelPoolSizeLabel = new JLabel("Traffic Pool: " + mTrafficChannelPoolSize.getValue() + " ");
|
||||||
{
|
|
||||||
mIgnoreDataCalls.setEnabled( enabled );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
add(mTrafficChannelPoolSizeLabel);
|
||||||
public void setItem( Channel item )
|
add(mTrafficChannelPoolSize);
|
||||||
{
|
}
|
||||||
super.setItem( item );
|
|
||||||
|
|
||||||
if( hasItem() )
|
@Override
|
||||||
{
|
public void save()
|
||||||
setControlsEnabled( true );
|
{
|
||||||
|
if(hasItem() && isModified())
|
||||||
|
{
|
||||||
|
DecodeConfigP25Phase1 p25 = new DecodeConfigP25Phase1();
|
||||||
|
|
||||||
DecodeConfiguration config = getItem().getDecodeConfiguration();
|
p25.setModulation((P25Decoder.Modulation)mComboModulation.getSelectedItem());
|
||||||
|
p25.setIgnoreDataCalls(mIgnoreDataCalls.isSelected());
|
||||||
|
p25.setTrafficChannelPoolSize(mTrafficChannelPoolSize.getValue());
|
||||||
|
|
||||||
if( config instanceof DecodeConfigP25Phase1 )
|
getItem().setDecodeConfiguration(p25);
|
||||||
{
|
}
|
||||||
DecodeConfigP25Phase1 p25 = (DecodeConfigP25Phase1)config;
|
|
||||||
|
|
||||||
mComboModulation.setSelectedItem( p25.getModulation() );
|
setModified(false);
|
||||||
mIgnoreDataCalls.setSelected( p25.getIgnoreDataCalls() );
|
}
|
||||||
mTrafficChannelPoolSize.setValue( p25.getTrafficChannelPoolSize() );
|
|
||||||
setModified( false );
|
@Override
|
||||||
}
|
public void validate(Editor<Channel> editor) throws EditorValidationException
|
||||||
else
|
{
|
||||||
{
|
/**
|
||||||
mComboModulation.setSelectedItem( P25Decoder.Modulation.C4FM );
|
* Enforce CQPSK modulation uses a Tuner Source for I/Q sample data
|
||||||
mIgnoreDataCalls.setSelected( false );
|
*/
|
||||||
mTrafficChannelPoolSize.setValue( DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT );
|
if(editor instanceof SourceConfigurationEditor)
|
||||||
setModified( true );
|
{
|
||||||
}
|
SourceConfiguration config = ((SourceConfigurationEditor)editor).getSourceConfiguration();
|
||||||
}
|
|
||||||
else
|
if(!(config instanceof SourceConfigTuner || config instanceof SourceConfigTunerMultipleFrequency))
|
||||||
{
|
{
|
||||||
setControlsEnabled( false );
|
throw new EditorValidationException(editor,
|
||||||
setModified( false );
|
"<html><body width='175'><h3>LSM Simulcast</h3>"
|
||||||
}
|
+ "<p>P25 LSM Simulcast decoder can only be used with "
|
||||||
}
|
+ "a tuner source. Please change the Source to use a tuner"
|
||||||
|
+ " or change the P25 Decoder to C4FM modulation</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(editor instanceof AuxDecodeConfigurationEditor)
|
||||||
|
{
|
||||||
|
AuxDecodeConfiguration config = ((AuxDecodeConfigurationEditor)editor).getConfiguration();
|
||||||
|
|
||||||
|
if(config != null && !config.getAuxDecoders().isEmpty())
|
||||||
|
{
|
||||||
|
throw new EditorValidationException(editor,
|
||||||
|
"<html><body width='175'><h3>Auxilary Decoders</h3>"
|
||||||
|
+ "<p>The auxiliary decoders work with analog audio and are not "
|
||||||
|
+ "compatible with the P25 Decoder. Please uncheck any auxiliary "
|
||||||
|
+ "decoders that you have selected for this channel</p>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setControlsEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
if(mComboModulation.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mComboModulation.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mTrafficChannelPoolSize.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mTrafficChannelPoolSize.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mIgnoreDataCalls.isEnabled() != enabled)
|
||||||
|
{
|
||||||
|
mIgnoreDataCalls.setEnabled(enabled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(Channel item)
|
||||||
|
{
|
||||||
|
super.setItem(item);
|
||||||
|
|
||||||
|
if(hasItem())
|
||||||
|
{
|
||||||
|
setControlsEnabled(true);
|
||||||
|
|
||||||
|
DecodeConfiguration config = getItem().getDecodeConfiguration();
|
||||||
|
|
||||||
|
if(config instanceof DecodeConfigP25Phase1)
|
||||||
|
{
|
||||||
|
DecodeConfigP25Phase1 p25 = (DecodeConfigP25Phase1)config;
|
||||||
|
|
||||||
|
mComboModulation.setSelectedItem(p25.getModulation());
|
||||||
|
mIgnoreDataCalls.setSelected(p25.getIgnoreDataCalls());
|
||||||
|
mTrafficChannelPoolSize.setValue(p25.getTrafficChannelPoolSize());
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mComboModulation.setSelectedItem(P25Decoder.Modulation.C4FM);
|
||||||
|
mIgnoreDataCalls.setSelected(false);
|
||||||
|
mTrafficChannelPoolSize.setValue(DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT);
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setControlsEnabled(false);
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -28,5 +28,6 @@ public enum PreferenceType
|
|||||||
DECODE_EVENT,
|
DECODE_EVENT,
|
||||||
DIRECTORY,
|
DIRECTORY,
|
||||||
IDENTIFIER,
|
IDENTIFIER,
|
||||||
|
MULTI_FREQUENCY,
|
||||||
TUNER;
|
TUNER;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -25,7 +25,8 @@ import io.github.dsheirer.preference.directory.DirectoryPreference;
|
|||||||
import io.github.dsheirer.preference.event.DecodeEventPreference;
|
import io.github.dsheirer.preference.event.DecodeEventPreference;
|
||||||
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
|
import io.github.dsheirer.preference.identifier.TalkgroupFormatPreference;
|
||||||
import io.github.dsheirer.preference.playlist.PlaylistPreference;
|
import io.github.dsheirer.preference.playlist.PlaylistPreference;
|
||||||
import io.github.dsheirer.preference.tuner.TunerPreference;
|
import io.github.dsheirer.preference.source.ChannelMultiFrequencyPreference;
|
||||||
|
import io.github.dsheirer.preference.source.TunerPreference;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -48,6 +49,7 @@ public class UserPreferences implements Listener<PreferenceType>
|
|||||||
{
|
{
|
||||||
private DecodeEventPreference mDecodeEventPreference;
|
private DecodeEventPreference mDecodeEventPreference;
|
||||||
private DirectoryPreference mDirectoryPreference;
|
private DirectoryPreference mDirectoryPreference;
|
||||||
|
private ChannelMultiFrequencyPreference mChannelMultiFrequencyPreference;
|
||||||
private PlaylistPreference mPlaylistPreference;
|
private PlaylistPreference mPlaylistPreference;
|
||||||
private TalkgroupFormatPreference mTalkgroupFormatPreference;
|
private TalkgroupFormatPreference mTalkgroupFormatPreference;
|
||||||
private TunerPreference mTunerPreference;
|
private TunerPreference mTunerPreference;
|
||||||
@@ -76,6 +78,14 @@ public class UserPreferences implements Listener<PreferenceType>
|
|||||||
return mDirectoryPreference;
|
return mDirectoryPreference;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple frequency channel source preferences
|
||||||
|
*/
|
||||||
|
public ChannelMultiFrequencyPreference getChannelMultiFrequencyPreference()
|
||||||
|
{
|
||||||
|
return mChannelMultiFrequencyPreference;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Playlist preferences
|
* Playlist preferences
|
||||||
*/
|
*/
|
||||||
@@ -106,7 +116,8 @@ public class UserPreferences implements Listener<PreferenceType>
|
|||||||
private void loadPreferenceTypes()
|
private void loadPreferenceTypes()
|
||||||
{
|
{
|
||||||
mDecodeEventPreference = new DecodeEventPreference(this);
|
mDecodeEventPreference = new DecodeEventPreference(this);
|
||||||
mDirectoryPreference = new DirectoryPreference(this::receive);
|
mDirectoryPreference = new DirectoryPreference(this);
|
||||||
|
mChannelMultiFrequencyPreference = new ChannelMultiFrequencyPreference(this);
|
||||||
mPlaylistPreference = new PlaylistPreference(this, mDirectoryPreference);
|
mPlaylistPreference = new PlaylistPreference(this, mDirectoryPreference);
|
||||||
mTalkgroupFormatPreference = new TalkgroupFormatPreference(this);
|
mTalkgroupFormatPreference = new TalkgroupFormatPreference(this);
|
||||||
mTunerPreference = new TunerPreference(this);
|
mTunerPreference = new TunerPreference(this);
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 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.preference.source;
|
||||||
|
|
||||||
|
import io.github.dsheirer.preference.Preference;
|
||||||
|
import io.github.dsheirer.preference.PreferenceType;
|
||||||
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preferences for multiple frequency tuner channel sources.
|
||||||
|
*/
|
||||||
|
public class ChannelMultiFrequencyPreference extends Preference
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(ChannelMultiFrequencyPreference.class);
|
||||||
|
private Preferences mPreferences = Preferences.userNodeForPackage(ChannelMultiFrequencyPreference.class);
|
||||||
|
private static final String PREFERENCE_KEY_ROTATION_DELAY = "rotation.delay";
|
||||||
|
private static final long DEFAULT_ROTATION_DELAY = 5000; //5 seconds
|
||||||
|
private Long mRotationDelay = DEFAULT_ROTATION_DELAY;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a tuner preference with the update listener
|
||||||
|
*
|
||||||
|
* @param updateListener
|
||||||
|
*/
|
||||||
|
public ChannelMultiFrequencyPreference(Listener<PreferenceType> updateListener)
|
||||||
|
{
|
||||||
|
super(updateListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PreferenceType getPreferenceType()
|
||||||
|
{
|
||||||
|
return PreferenceType.MULTI_FREQUENCY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotation delay value in milliseconds. This value is used for channel configurations where there source
|
||||||
|
* defines multiple frequencies for a system that employs rotating control channels.
|
||||||
|
*/
|
||||||
|
public long getRotationDelay()
|
||||||
|
{
|
||||||
|
if(mRotationDelay == null)
|
||||||
|
{
|
||||||
|
mRotationDelay = mPreferences.getLong(PREFERENCE_KEY_ROTATION_DELAY, DEFAULT_ROTATION_DELAY);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mRotationDelay;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the rotation delay value in milliseconds.
|
||||||
|
* @param rotationDelay in milliseconds (1,000 - 60,000)
|
||||||
|
*/
|
||||||
|
public void setRotationDelay(long rotationDelay)
|
||||||
|
{
|
||||||
|
if(1000 <= rotationDelay && rotationDelay <= 60000)
|
||||||
|
{
|
||||||
|
mRotationDelay = rotationDelay;
|
||||||
|
mPreferences.putLong(PREFERENCE_KEY_ROTATION_DELAY, mRotationDelay);
|
||||||
|
notifyPreferenceUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets the rotation delay and removes it from the preferences store, so that it can be recreated with
|
||||||
|
* a default value.
|
||||||
|
*/
|
||||||
|
public void resetRotationDelay()
|
||||||
|
{
|
||||||
|
mPreferences.remove(PREFERENCE_KEY_ROTATION_DELAY);
|
||||||
|
mRotationDelay = null;
|
||||||
|
notifyPreferenceUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
* *****************************************************************************
|
* *****************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.github.dsheirer.preference.tuner;
|
package io.github.dsheirer.preference.source;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Channelizer Type - identifies the type of channelizer that the tuner will use to provide DDR channels
|
* Channelizer Type - identifies the type of channelizer that the tuner will use to provide DDR channels
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
* *****************************************************************************
|
* *****************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.github.dsheirer.preference.tuner;
|
package io.github.dsheirer.preference.source;
|
||||||
|
|
||||||
import io.github.dsheirer.preference.Preference;
|
import io.github.dsheirer.preference.Preference;
|
||||||
import io.github.dsheirer.preference.PreferenceType;
|
import io.github.dsheirer.preference.PreferenceType;
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* SDR Trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
package io.github.dsheirer.sample;
|
package io.github.dsheirer.sample;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
@@ -25,8 +30,23 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
*/
|
*/
|
||||||
public class Broadcaster<T> implements Listener<T>
|
public class Broadcaster<T> implements Listener<T>
|
||||||
{
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(Broadcaster.class);
|
||||||
|
private boolean mDebug;
|
||||||
|
|
||||||
protected List<Listener<T>> mListeners = new CopyOnWriteArrayList<>();
|
protected List<Listener<T>> mListeners = new CopyOnWriteArrayList<>();
|
||||||
|
|
||||||
|
public Broadcaster()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns on/off debugging to troubleshoot and log which listeners are receiving an item.
|
||||||
|
*/
|
||||||
|
public void setDebug(boolean debug)
|
||||||
|
{
|
||||||
|
mDebug = debug;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements the Listener<T> interface to receive an element and broadcast that element to all registered
|
* Implements the Listener<T> interface to receive an element and broadcast that element to all registered
|
||||||
* listeners.
|
* listeners.
|
||||||
@@ -116,9 +136,21 @@ public class Broadcaster<T> implements Listener<T>
|
|||||||
*/
|
*/
|
||||||
public void broadcast(T t)
|
public void broadcast(T t)
|
||||||
{
|
{
|
||||||
for(Listener<T> listener : mListeners)
|
if(mDebug)
|
||||||
{
|
{
|
||||||
listener.receive(t);
|
for(Listener<T> listener : mListeners)
|
||||||
|
{
|
||||||
|
mLog.debug("Sending [" + t + "] to listener [" + listener.getClass() + "]");
|
||||||
|
listener.receive(t);
|
||||||
|
mLog.debug("Finished sending to listener [" + listener.getClass() + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for(Listener<T> listener : mListeners)
|
||||||
|
{
|
||||||
|
listener.receive(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,29 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* SDR Trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
package io.github.dsheirer.source;
|
package io.github.dsheirer.source;
|
||||||
|
|
||||||
import io.github.dsheirer.module.Module;
|
import io.github.dsheirer.module.Module;
|
||||||
import io.github.dsheirer.sample.IOverflowListener;
|
import io.github.dsheirer.sample.IOverflowListener;
|
||||||
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.sample.SampleType;
|
import io.github.dsheirer.sample.SampleType;
|
||||||
|
import io.github.dsheirer.source.heartbeat.Heartbeat;
|
||||||
import io.github.dsheirer.source.heartbeat.HeartbeatManager;
|
import io.github.dsheirer.source.heartbeat.HeartbeatManager;
|
||||||
import io.github.dsheirer.source.heartbeat.IHeartbeatProvider;
|
import io.github.dsheirer.source.heartbeat.IHeartbeatProvider;
|
||||||
|
|
||||||
@@ -39,8 +43,29 @@ public abstract class Source extends Module implements ISourceEventListener, ISo
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffer overflow listener
|
||||||
|
*/
|
||||||
protected IOverflowListener mOverflowListener;
|
protected IOverflowListener mOverflowListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the listener to receive heartbeats from this source
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addHeartbeatListener(Listener<Heartbeat> listener)
|
||||||
|
{
|
||||||
|
getHeartbeatManager().addHeartbeatListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the listener from receiving heartbeats from this source
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeHeartbeatListener(Listener<Heartbeat> listener)
|
||||||
|
{
|
||||||
|
getHeartbeatManager().removeHeartbeatListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates the type of samples provided by this source: real or complex
|
* Indicates the type of samples provided by this source: real or complex
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* SDR Trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014-2016 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
package io.github.dsheirer.source;
|
package io.github.dsheirer.source;
|
||||||
|
|
||||||
import io.github.dsheirer.controller.channel.Channel;
|
import io.github.dsheirer.controller.channel.Channel;
|
||||||
@@ -23,6 +25,7 @@ import io.github.dsheirer.gui.editor.EmptyEditor;
|
|||||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||||
import io.github.dsheirer.source.mixer.MixerSourceEditor;
|
import io.github.dsheirer.source.mixer.MixerSourceEditor;
|
||||||
import io.github.dsheirer.source.tuner.TunerSourceEditor;
|
import io.github.dsheirer.source.tuner.TunerSourceEditor;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerSourceMultipleFrequencyEditor;
|
||||||
import net.miginfocom.swing.MigLayout;
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
import javax.swing.DefaultComboBoxModel;
|
import javax.swing.DefaultComboBoxModel;
|
||||||
@@ -37,99 +40,106 @@ public class SourceConfigurationEditor extends Editor<Channel>
|
|||||||
private JComboBox<SourceType> mComboSources;
|
private JComboBox<SourceType> mComboSources;
|
||||||
private MixerSourceEditor mMixerSourceEditor;
|
private MixerSourceEditor mMixerSourceEditor;
|
||||||
private TunerSourceEditor mTunerSourceEditor;
|
private TunerSourceEditor mTunerSourceEditor;
|
||||||
|
private TunerSourceMultipleFrequencyEditor mTunerSourceMultipleFrequencyEditor;
|
||||||
private Editor<Channel> mCurrentEditor;
|
private Editor<Channel> mCurrentEditor;
|
||||||
|
|
||||||
public SourceConfigurationEditor( SourceManager sourceManager )
|
public SourceConfigurationEditor(SourceManager sourceManager)
|
||||||
{
|
{
|
||||||
mMixerSourceEditor = new MixerSourceEditor( sourceManager );
|
mMixerSourceEditor = new MixerSourceEditor(sourceManager);
|
||||||
mMixerSourceEditor.setSaveRequestListener( this );
|
mMixerSourceEditor.setSaveRequestListener(this);
|
||||||
|
|
||||||
mTunerSourceEditor = new TunerSourceEditor(sourceManager.getTunerModel());
|
mTunerSourceEditor = new TunerSourceEditor(sourceManager.getTunerModel());
|
||||||
mTunerSourceEditor.setSaveRequestListener( this );
|
mTunerSourceEditor.setSaveRequestListener(this);
|
||||||
|
|
||||||
init();
|
mTunerSourceMultipleFrequencyEditor = new TunerSourceMultipleFrequencyEditor(sourceManager.getTunerModel());
|
||||||
}
|
mTunerSourceMultipleFrequencyEditor.setSaveRequestListener(this);
|
||||||
|
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
public SourceConfiguration getSourceConfiguration()
|
public SourceConfiguration getSourceConfiguration()
|
||||||
{
|
{
|
||||||
return hasItem() ? getItem().getSourceConfiguration() : null;
|
return hasItem() ? getItem().getSourceConfiguration() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init()
|
private void init()
|
||||||
{
|
{
|
||||||
setLayout( new MigLayout( "wrap 2", "[][grow,fill]", "[align top][grow]" ) );
|
setLayout(new MigLayout("wrap 2", "[][grow,fill]", "[align top][grow,fill]"));
|
||||||
|
|
||||||
mComboSources = new JComboBox<SourceType>();
|
mComboSources = new JComboBox<SourceType>();
|
||||||
mComboSources.setModel( new DefaultComboBoxModel<SourceType>( SourceType.getTypes() ) );
|
mComboSources.setModel(new DefaultComboBoxModel<SourceType>(SourceType.getTypes()));
|
||||||
mComboSources.addActionListener( new ActionListener()
|
mComboSources.addActionListener(new ActionListener()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed( ActionEvent e )
|
public void actionPerformed(ActionEvent e)
|
||||||
{
|
{
|
||||||
SourceType selected = (SourceType)mComboSources.getSelectedItem();
|
SourceType selected = (SourceType) mComboSources.getSelectedItem();
|
||||||
|
|
||||||
switch( selected )
|
switch(selected)
|
||||||
{
|
{
|
||||||
case MIXER:
|
case MIXER:
|
||||||
setEditor( mMixerSourceEditor );
|
setEditor(mMixerSourceEditor);
|
||||||
break;
|
break;
|
||||||
case TUNER:
|
case TUNER:
|
||||||
setEditor( mTunerSourceEditor );
|
setEditor(mTunerSourceEditor);
|
||||||
break;
|
break;
|
||||||
default:
|
case TUNER_MULTIPLE_FREQUENCIES:
|
||||||
setEditor( new EmptyEditor<Channel>() );
|
setEditor(mTunerSourceMultipleFrequencyEditor);
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
}
|
setEditor(new EmptyEditor<Channel>());
|
||||||
});
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
add( mComboSources );
|
add(mComboSources);
|
||||||
|
|
||||||
mCurrentEditor = mTunerSourceEditor;
|
mCurrentEditor = mTunerSourceEditor;
|
||||||
add( mCurrentEditor );
|
add(mCurrentEditor, "span 1 2");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setEditor( Editor<Channel> editor )
|
private void setEditor(Editor<Channel> editor)
|
||||||
{
|
{
|
||||||
if( mCurrentEditor != editor )
|
if(mCurrentEditor != editor)
|
||||||
{
|
{
|
||||||
//Set channel to null to force a save prompt as required
|
//Set channel to null to force a save prompt as required
|
||||||
mCurrentEditor.setItem( null );
|
mCurrentEditor.setItem(null);
|
||||||
|
|
||||||
remove( mCurrentEditor );
|
remove(mCurrentEditor);
|
||||||
mCurrentEditor = editor;
|
mCurrentEditor = editor;
|
||||||
mCurrentEditor.setSaveRequestListener( this );
|
mCurrentEditor.setSaveRequestListener(this);
|
||||||
add( mCurrentEditor );
|
add(mCurrentEditor, "span 1 2");
|
||||||
|
|
||||||
mCurrentEditor.setItem( getItem() );
|
mCurrentEditor.setItem(getItem());
|
||||||
|
|
||||||
revalidate();
|
revalidate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void save()
|
public void save()
|
||||||
{
|
{
|
||||||
if( hasItem() )
|
if(hasItem())
|
||||||
{
|
{
|
||||||
mCurrentEditor.save();
|
mCurrentEditor.save();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the channel configuration. Note: this method must be invoked from
|
* Sets the channel configuration. Note: this method must be invoked from
|
||||||
* the swing event dispatch thread.
|
* the swing event dispatch thread.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setItem( Channel channel )
|
public void setItem(Channel channel)
|
||||||
{
|
{
|
||||||
super.setItem( channel );
|
super.setItem(channel);
|
||||||
|
|
||||||
if( hasItem() )
|
if(hasItem())
|
||||||
{
|
{
|
||||||
mComboSources.setSelectedItem( getItem().getSourceConfiguration().getSourceType() );
|
mComboSources.setSelectedItem(getItem().getSourceConfiguration().getSourceType());
|
||||||
repaint();
|
repaint();
|
||||||
mCurrentEditor.setItem( channel );
|
mCurrentEditor.setItem(channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,8 +15,8 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.source;
|
package io.github.dsheirer.source;
|
||||||
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@@ -31,6 +32,8 @@ public class SourceEvent
|
|||||||
NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED,
|
NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED,
|
||||||
NOTIFICATION_FREQUENCY_CHANGE,
|
NOTIFICATION_FREQUENCY_CHANGE,
|
||||||
NOTIFICATION_FREQUENCY_CORRECTION_CHANGE,
|
NOTIFICATION_FREQUENCY_CORRECTION_CHANGE,
|
||||||
|
NOTIFICATION_FREQUENCY_ROTATION_SUCCESS,
|
||||||
|
NOTIFICATION_FREQUENCY_ROTATION_FAILURE,
|
||||||
NOTIFICATION_MEASURED_FREQUENCY_ERROR,
|
NOTIFICATION_MEASURED_FREQUENCY_ERROR,
|
||||||
NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED,
|
NOTIFICATION_MEASURED_FREQUENCY_ERROR_SYNC_LOCKED,
|
||||||
NOTIFICATION_SAMPLE_RATE_CHANGE,
|
NOTIFICATION_SAMPLE_RATE_CHANGE,
|
||||||
@@ -38,6 +41,7 @@ public class SourceEvent
|
|||||||
|
|
||||||
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
|
REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE,
|
||||||
REQUEST_FREQUENCY_CHANGE,
|
REQUEST_FREQUENCY_CHANGE,
|
||||||
|
REQUEST_FREQUENCY_ROTATION,
|
||||||
REQUEST_SOURCE_DISPOSE,
|
REQUEST_SOURCE_DISPOSE,
|
||||||
REQUEST_START_SAMPLE_STREAM,
|
REQUEST_START_SAMPLE_STREAM,
|
||||||
REQUEST_STOP_SAMPLE_STREAM;
|
REQUEST_STOP_SAMPLE_STREAM;
|
||||||
@@ -64,6 +68,14 @@ public class SourceEvent
|
|||||||
mEventDescription = eventDescription;
|
mEventDescription = eventDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor. Use the static constructor methods to create an event.
|
||||||
|
*/
|
||||||
|
private SourceEvent(Event event)
|
||||||
|
{
|
||||||
|
this(event, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor. Use the static constructor methods to create an event.
|
* Private constructor. Use the static constructor methods to create an event.
|
||||||
*/
|
*/
|
||||||
@@ -175,9 +187,9 @@ public class SourceEvent
|
|||||||
*
|
*
|
||||||
* @param frequency in hertz
|
* @param frequency in hertz
|
||||||
*/
|
*/
|
||||||
public static SourceEvent frequencyChange(long frequency)
|
public static SourceEvent frequencyChange(Source source, long frequency)
|
||||||
{
|
{
|
||||||
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, frequency);
|
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, source, frequency);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -186,9 +198,9 @@ public class SourceEvent
|
|||||||
* @param frequency in hertz
|
* @param frequency in hertz
|
||||||
* @param eventDescription to add a tag to the event
|
* @param eventDescription to add a tag to the event
|
||||||
*/
|
*/
|
||||||
public static SourceEvent frequencyChange(long frequency, String eventDescription)
|
public static SourceEvent frequencyChange(Source source, long frequency, String eventDescription)
|
||||||
{
|
{
|
||||||
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, frequency, eventDescription);
|
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_CHANGE, source, frequency, eventDescription);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -338,6 +350,33 @@ public class SourceEvent
|
|||||||
return new SourceEvent(Event.REQUEST_SOURCE_DISPOSE, source);
|
return new SourceEvent(Event.REQUEST_SOURCE_DISPOSE, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a request to cycle to the next source frequency. This is normally used for decoders to request the
|
||||||
|
* next frequency in a list when a multiple-frequency source configuration is defined.
|
||||||
|
*/
|
||||||
|
public static SourceEvent frequencyRotationRequest()
|
||||||
|
{
|
||||||
|
return new SourceEvent(Event.REQUEST_FREQUENCY_ROTATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a notification that the request to rotate the source frequency to the next frequency in the list
|
||||||
|
* was successful.
|
||||||
|
*/
|
||||||
|
public static SourceEvent frequencyRotationSuccessNotification(Source source, long frequency)
|
||||||
|
{
|
||||||
|
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_ROTATION_SUCCESS, source, frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a notification that the request to rotate the source frequency to the next frequency in the list
|
||||||
|
* was unsuccessful.
|
||||||
|
*/
|
||||||
|
public static SourceEvent frequencyRotationFailureNotification(Source source, long frequency)
|
||||||
|
{
|
||||||
|
return new SourceEvent(Event.NOTIFICATION_FREQUENCY_ROTATION_FAILURE, source, frequency);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -22,12 +22,16 @@ package io.github.dsheirer.source;
|
|||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.settings.SettingsManager;
|
import io.github.dsheirer.settings.SettingsManager;
|
||||||
import io.github.dsheirer.source.config.SourceConfigTuner;
|
import io.github.dsheirer.source.config.SourceConfigTuner;
|
||||||
|
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
|
||||||
import io.github.dsheirer.source.config.SourceConfiguration;
|
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||||
import io.github.dsheirer.source.mixer.MixerManager;
|
import io.github.dsheirer.source.mixer.MixerManager;
|
||||||
import io.github.dsheirer.source.recording.RecordingSourceManager;
|
import io.github.dsheirer.source.recording.RecordingSourceManager;
|
||||||
import io.github.dsheirer.source.tuner.TunerManager;
|
import io.github.dsheirer.source.tuner.TunerManager;
|
||||||
import io.github.dsheirer.source.tuner.TunerModel;
|
import io.github.dsheirer.source.tuner.TunerModel;
|
||||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||||
|
import io.github.dsheirer.source.tuner.channel.MultiFrequencyTunerChannelSource;
|
||||||
|
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||||
|
import io.github.dsheirer.source.tuner.channel.TunerChannelSource;
|
||||||
|
|
||||||
public class SourceManager
|
public class SourceManager
|
||||||
{
|
{
|
||||||
@@ -86,7 +90,29 @@ public class SourceManager
|
|||||||
retVal = mMixerManager.getSource(config);
|
retVal = mMixerManager.getSource(config);
|
||||||
break;
|
break;
|
||||||
case TUNER:
|
case TUNER:
|
||||||
retVal = mTunerModel.getSource((SourceConfigTuner) config, channelSpecification);
|
if(config instanceof SourceConfigTuner)
|
||||||
|
{
|
||||||
|
SourceConfigTuner sourceConfigTuner = (SourceConfigTuner)config;
|
||||||
|
TunerChannel tunerChannel = sourceConfigTuner.getTunerChannel(channelSpecification.getBandwidth());
|
||||||
|
String preferredTuner = sourceConfigTuner.getPreferredTuner();
|
||||||
|
retVal = mTunerModel.getSource(tunerChannel, channelSpecification, preferredTuner);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TUNER_MULTIPLE_FREQUENCIES:
|
||||||
|
if(config instanceof SourceConfigTunerMultipleFrequency)
|
||||||
|
{
|
||||||
|
SourceConfigTunerMultipleFrequency sourceConfigTuner = (SourceConfigTunerMultipleFrequency)config;
|
||||||
|
TunerChannel tunerChannel = sourceConfigTuner.getFirstTunerChannel(channelSpecification.getBandwidth());
|
||||||
|
String preferredTuner = sourceConfigTuner.getPreferredTuner();
|
||||||
|
|
||||||
|
Source source = mTunerModel.getSource(tunerChannel, channelSpecification, preferredTuner);
|
||||||
|
|
||||||
|
if(source instanceof TunerChannelSource)
|
||||||
|
{
|
||||||
|
retVal = new MultiFrequencyTunerChannelSource(getTunerModel(), (TunerChannelSource)source,
|
||||||
|
sourceConfigTuner.getFrequencies(), channelSpecification, sourceConfigTuner.getPreferredTuner());
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case RECORDING:
|
case RECORDING:
|
||||||
retVal = mRecordingSourceManager.getSource(config, channelSpecification);
|
retVal = mRecordingSourceManager.getSource(config, channelSpecification);
|
||||||
|
|||||||
@@ -1,54 +1,51 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdr-trunk
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
* it under the terms of the GNU General Public License as published by
|
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* later version.
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
* 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/>
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.source;
|
package io.github.dsheirer.source;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source Configuration Types enumeration.
|
||||||
|
*/
|
||||||
public enum SourceType
|
public enum SourceType
|
||||||
{
|
{
|
||||||
NONE( "No Source" ),
|
NONE("No Source"),
|
||||||
MIXER( "Sound Card" ),
|
MIXER("Sound Card"),
|
||||||
TUNER( "Tuner" ),
|
TUNER("Tuner"),
|
||||||
RECORDING( "IQ Recording" );
|
TUNER_MULTIPLE_FREQUENCIES("Tuner - Multiple Frequencies"),
|
||||||
|
RECORDING("IQ Recording");
|
||||||
|
|
||||||
private String mDisplayString;
|
private String mDisplayString;
|
||||||
|
|
||||||
SourceType( String displayString )
|
SourceType(String displayString)
|
||||||
{
|
{
|
||||||
mDisplayString = displayString;
|
mDisplayString = displayString;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SourceType[] getTypes()
|
public static SourceType[] getTypes()
|
||||||
{
|
{
|
||||||
SourceType[] types = new SourceType[ 2 ];
|
SourceType[] types = new SourceType[3];
|
||||||
types[ 0 ] = SourceType.TUNER;
|
types[0] = SourceType.TUNER;
|
||||||
types[ 1 ] = SourceType.MIXER;
|
types[1] = SourceType.TUNER_MULTIPLE_FREQUENCIES;
|
||||||
|
types[2] = SourceType.MIXER;
|
||||||
|
|
||||||
return types;
|
return types;
|
||||||
}
|
|
||||||
|
|
||||||
public String getDisplayString()
|
|
||||||
{
|
|
||||||
return mDisplayString;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return mDisplayString;
|
return mDisplayString;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* SDR Trunk
|
* sdr-trunk
|
||||||
* Copyright (C) 2014 Dennis Sheirer
|
* Copyright (C) 2014-2018 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
||||||
* it under the terms of the GNU General Public License as published by
|
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* later version.
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||||
* 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/>
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
package io.github.dsheirer.source.config;
|
package io.github.dsheirer.source.config;
|
||||||
|
|
||||||
@@ -21,64 +19,74 @@ import io.github.dsheirer.source.SourceType;
|
|||||||
|
|
||||||
public class SourceConfigFactory
|
public class SourceConfigFactory
|
||||||
{
|
{
|
||||||
public static SourceConfiguration getDefaultSourceConfiguration()
|
public static SourceConfiguration getDefaultSourceConfiguration()
|
||||||
{
|
{
|
||||||
return getSourceConfiguration( SourceType.TUNER );
|
return getSourceConfiguration(SourceType.TUNER);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SourceConfiguration getSourceConfiguration( SourceType source )
|
public static SourceConfiguration getSourceConfiguration(SourceType source)
|
||||||
{
|
{
|
||||||
SourceConfiguration retVal;
|
SourceConfiguration retVal;
|
||||||
|
|
||||||
switch( source )
|
switch(source)
|
||||||
{
|
{
|
||||||
case TUNER:
|
case TUNER:
|
||||||
retVal = new SourceConfigTuner();
|
retVal = new SourceConfigTuner();
|
||||||
break;
|
break;
|
||||||
case MIXER:
|
case TUNER_MULTIPLE_FREQUENCIES:
|
||||||
retVal = new SourceConfigMixer();
|
retVal = new SourceConfigTunerMultipleFrequency();
|
||||||
break;
|
break;
|
||||||
case NONE:
|
case MIXER:
|
||||||
default:
|
retVal = new SourceConfigMixer();
|
||||||
retVal = new SourceConfigNone();
|
break;
|
||||||
break;
|
case NONE:
|
||||||
}
|
default:
|
||||||
|
retVal = new SourceConfigNone();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a copy of the configuration
|
* Creates a copy of the configuration
|
||||||
*/
|
*/
|
||||||
public static SourceConfiguration copy( SourceConfiguration config )
|
public static SourceConfiguration copy(SourceConfiguration config)
|
||||||
{
|
{
|
||||||
if( config != null )
|
if(config != null)
|
||||||
{
|
{
|
||||||
switch( config.getSourceType() )
|
switch(config.getSourceType())
|
||||||
{
|
{
|
||||||
case MIXER:
|
case MIXER:
|
||||||
SourceConfigMixer originalMixer = (SourceConfigMixer)config;
|
SourceConfigMixer originalMixer = (SourceConfigMixer) config;
|
||||||
SourceConfigMixer copyMixer = new SourceConfigMixer();
|
SourceConfigMixer copyMixer = new SourceConfigMixer();
|
||||||
copyMixer.setChannel( originalMixer.getChannel() );
|
copyMixer.setChannel(originalMixer.getChannel());
|
||||||
copyMixer.setMixer( originalMixer.getMixer() );
|
copyMixer.setMixer(originalMixer.getMixer());
|
||||||
return copyMixer;
|
return copyMixer;
|
||||||
case RECORDING:
|
case RECORDING:
|
||||||
SourceConfigRecording originalRec = (SourceConfigRecording)config;
|
SourceConfigRecording originalRec = (SourceConfigRecording) config;
|
||||||
SourceConfigRecording copyRec = new SourceConfigRecording();
|
SourceConfigRecording copyRec = new SourceConfigRecording();
|
||||||
copyRec.setFrequency( originalRec.getFrequency() );
|
copyRec.setFrequency(originalRec.getFrequency());
|
||||||
copyRec.setRecordingAlias( originalRec.getRecordingAlias() );
|
copyRec.setRecordingAlias(originalRec.getRecordingAlias());
|
||||||
return copyRec;
|
return copyRec;
|
||||||
case TUNER:
|
case TUNER:
|
||||||
SourceConfigTuner originalTuner = (SourceConfigTuner)config;
|
SourceConfigTuner originalTuner = (SourceConfigTuner) config;
|
||||||
SourceConfigTuner copyTuner = new SourceConfigTuner();
|
SourceConfigTuner copyTuner = new SourceConfigTuner();
|
||||||
copyTuner.setFrequency( originalTuner.getFrequency() );
|
copyTuner.setPreferredTuner(originalTuner.getPreferredTuner());
|
||||||
return copyTuner;
|
copyTuner.setFrequency(originalTuner.getFrequency());
|
||||||
case NONE:
|
return copyTuner;
|
||||||
default:
|
case TUNER_MULTIPLE_FREQUENCIES:
|
||||||
return new SourceConfigNone();
|
SourceConfigTunerMultipleFrequency originalMulti = (SourceConfigTunerMultipleFrequency) config;
|
||||||
}
|
SourceConfigTunerMultipleFrequency copyMulti = new SourceConfigTunerMultipleFrequency();
|
||||||
}
|
copyMulti.setPreferredTuner(originalMulti.getPreferredTuner());
|
||||||
|
copyMulti.setFrequencies(originalMulti.getFrequencies());
|
||||||
|
return copyMulti;
|
||||||
|
case NONE:
|
||||||
|
default:
|
||||||
|
return new SourceConfigNone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,22 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* SDR Trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
******************************************************************************/
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
package io.github.dsheirer.source.config;
|
package io.github.dsheirer.source.config;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
@@ -31,7 +33,6 @@ public class SourceConfigTuner extends SourceConfiguration
|
|||||||
private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
|
private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat("0.00000");
|
||||||
|
|
||||||
private long mFrequency = 0;
|
private long mFrequency = 0;
|
||||||
private int mBandwidth = 12500;
|
|
||||||
private String mPreferredTuner;
|
private String mPreferredTuner;
|
||||||
|
|
||||||
public SourceConfigTuner()
|
public SourceConfigTuner()
|
||||||
@@ -42,9 +43,7 @@ public class SourceConfigTuner extends SourceConfiguration
|
|||||||
public SourceConfigTuner(TunerChannel tunerChannel)
|
public SourceConfigTuner(TunerChannel tunerChannel)
|
||||||
{
|
{
|
||||||
this();
|
this();
|
||||||
|
|
||||||
mFrequency = tunerChannel.getFrequency();
|
mFrequency = tunerChannel.getFrequency();
|
||||||
mBandwidth = tunerChannel.getBandwidth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@JacksonXmlProperty(isAttribute = true, localName = "frequency")
|
@JacksonXmlProperty(isAttribute = true, localName = "frequency")
|
||||||
@@ -94,8 +93,8 @@ public class SourceConfigTuner extends SourceConfiguration
|
|||||||
}
|
}
|
||||||
|
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
public TunerChannel getTunerChannel()
|
public TunerChannel getTunerChannel(int bandwidth)
|
||||||
{
|
{
|
||||||
return new TunerChannel(mFrequency, mBandwidth);
|
return new TunerChannel(mFrequency, bandwidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.source.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonSubTypes;
|
||||||
|
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
|
||||||
|
import io.github.dsheirer.source.SourceType;
|
||||||
|
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple frequency tuner source configuration is used for a system with a rolling/rotating control
|
||||||
|
* channel where the control channel rotates across a pre-defined set of channel frequencies.
|
||||||
|
*/
|
||||||
|
@JsonSubTypes.Type(value = SourceConfigTunerMultipleFrequency.class, name = "sourceConfigTunerMultipleFrequency")
|
||||||
|
public class SourceConfigTunerMultipleFrequency extends SourceConfiguration
|
||||||
|
{
|
||||||
|
private List<Long> mFrequencies = new ArrayList<>();
|
||||||
|
private String mPreferredTuner;
|
||||||
|
|
||||||
|
public SourceConfigTunerMultipleFrequency()
|
||||||
|
{
|
||||||
|
super(SourceType.TUNER_MULTIPLE_FREQUENCIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List of frequencies for this configuration
|
||||||
|
*/
|
||||||
|
@JacksonXmlProperty(isAttribute = false, localName = "frequency")
|
||||||
|
public List<Long> getFrequencies()
|
||||||
|
{
|
||||||
|
return mFrequencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the list of frequencies for this configuration
|
||||||
|
*/
|
||||||
|
public void setFrequencies(List<Long> frequencies)
|
||||||
|
{
|
||||||
|
mFrequencies = frequencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this configuration has more than one frequency specified
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean hasMultipleFrequencies()
|
||||||
|
{
|
||||||
|
return mFrequencies.size() > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a frequency to the list
|
||||||
|
*/
|
||||||
|
public void addFrequency(long frequency)
|
||||||
|
{
|
||||||
|
mFrequencies.add(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preferred tuner to use for this configuration.
|
||||||
|
* @return tuner name or null
|
||||||
|
*/
|
||||||
|
@JacksonXmlProperty(isAttribute = true, localName = "preferred_tuner")
|
||||||
|
public String getPreferredTuner()
|
||||||
|
{
|
||||||
|
return mPreferredTuner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if this configuration has a specified preferred tuner
|
||||||
|
*/
|
||||||
|
@JsonIgnore
|
||||||
|
public boolean hasPreferredTuner()
|
||||||
|
{
|
||||||
|
return mPreferredTuner != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the preferred tuner to use for this configuration
|
||||||
|
* @param preferredTuner to use, if available
|
||||||
|
*/
|
||||||
|
public void setPreferredTuner(String preferredTuner)
|
||||||
|
{
|
||||||
|
mPreferredTuner = preferredTuner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
@Override
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return "Frequencies [" + mFrequencies.size() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonIgnore
|
||||||
|
public TunerChannel getFirstTunerChannel(int bandwidth)
|
||||||
|
{
|
||||||
|
if(getFrequencies().size() > 0)
|
||||||
|
{
|
||||||
|
return new TunerChannel(getFrequencies().get(0), bandwidth);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new TunerChannel(0l, bandwidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -30,7 +30,7 @@ import io.github.dsheirer.source.SourceType;
|
|||||||
@JsonSubTypes.Type(value = SourceConfigNone.class, name = "sourceConfigNone"),
|
@JsonSubTypes.Type(value = SourceConfigNone.class, name = "sourceConfigNone"),
|
||||||
@JsonSubTypes.Type(value = SourceConfigRecording.class, name = "sourceConfigRecording"),
|
@JsonSubTypes.Type(value = SourceConfigRecording.class, name = "sourceConfigRecording"),
|
||||||
@JsonSubTypes.Type(value = SourceConfigTuner.class, name = "sourceConfigTuner"),
|
@JsonSubTypes.Type(value = SourceConfigTuner.class, name = "sourceConfigTuner"),
|
||||||
})
|
@JsonSubTypes.Type(value = SourceConfigTunerMultipleFrequency.class, name = "sourceConfigTunerMultipleFrequency")})
|
||||||
@JacksonXmlRootElement(localName = "source_configuration")
|
@JacksonXmlRootElement(localName = "source_configuration")
|
||||||
public class SourceConfiguration extends Configuration
|
public class SourceConfiguration extends Configuration
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,11 +15,22 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.source.heartbeat;
|
package io.github.dsheirer.source.heartbeat;
|
||||||
|
|
||||||
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
|
||||||
public interface IHeartbeatProvider
|
public interface IHeartbeatProvider
|
||||||
{
|
{
|
||||||
public HeartbeatManager getHeartbeatManager();
|
/**
|
||||||
|
* Adds the listener to receive heartbeats
|
||||||
|
*/
|
||||||
|
void addHeartbeatListener(Listener<Heartbeat> listener);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the listener from receiving heartbeats
|
||||||
|
* @param listener
|
||||||
|
*/
|
||||||
|
void removeHeartbeatListener(Listener<Heartbeat> listener);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* ******************************************************************************
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
package io.github.dsheirer.source.tuner;
|
package io.github.dsheirer.source.tuner;
|
||||||
|
|
||||||
import io.github.dsheirer.preference.UserPreferences;
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
import io.github.dsheirer.preference.tuner.ChannelizerType;
|
import io.github.dsheirer.preference.source.ChannelizerType;
|
||||||
import io.github.dsheirer.sample.Broadcaster;
|
import io.github.dsheirer.sample.Broadcaster;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.source.ISourceEventProcessor;
|
import io.github.dsheirer.source.ISourceEventProcessor;
|
||||||
|
|||||||
@@ -1,25 +1,28 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* sdr-trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
* it under the terms of the GNU General Public License as published by
|
||||||
* later version.
|
* 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
|
* This program is distributed in the hope that it will be useful,
|
||||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
* 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.
|
* You should have received a copy of the GNU General Public License
|
||||||
* If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.source.tuner;
|
package io.github.dsheirer.source.tuner;
|
||||||
|
|
||||||
import io.github.dsheirer.properties.SystemProperties;
|
import io.github.dsheirer.properties.SystemProperties;
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
import io.github.dsheirer.source.Source;
|
import io.github.dsheirer.source.Source;
|
||||||
import io.github.dsheirer.source.SourceException;
|
import io.github.dsheirer.source.SourceException;
|
||||||
import io.github.dsheirer.source.config.SourceConfigTuner;
|
|
||||||
import io.github.dsheirer.source.tuner.TunerEvent.Event;
|
import io.github.dsheirer.source.tuner.TunerEvent.Event;
|
||||||
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
import io.github.dsheirer.source.tuner.channel.ChannelSpecification;
|
||||||
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
import io.github.dsheirer.source.tuner.channel.TunerChannel;
|
||||||
@@ -335,62 +338,60 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates current tuners to get a tuner channel source for the frequency
|
* Iterates current tuners to get a tuner channel source for the specified frequency and bandwidth
|
||||||
* specified in the channel config's source config object.
|
|
||||||
*
|
*
|
||||||
* Returns null if no tuner can source the channel
|
* Returns null if no tuner can source the channel
|
||||||
*/
|
*/
|
||||||
public Source getSource(SourceConfigTuner config, ChannelSpecification channelSpecification)
|
public Source getSource(TunerChannel tunerChannel, ChannelSpecification channelSpecification, String preferredTuner)
|
||||||
{
|
{
|
||||||
TunerChannelSource retVal = null;
|
TunerChannelSource source = null;
|
||||||
|
|
||||||
TunerChannel tunerChannel = config.getTunerChannel();
|
if(tunerChannel != null && channelSpecification != null)
|
||||||
|
|
||||||
tunerChannel.setBandwidth(channelSpecification.getBandwidth());
|
|
||||||
|
|
||||||
Iterator<Tuner> it = mTuners.iterator();
|
|
||||||
|
|
||||||
Tuner tuner;
|
|
||||||
|
|
||||||
if(config.hasPreferredTuner())
|
|
||||||
{
|
{
|
||||||
tuner = getTuner(config.getPreferredTuner());
|
Iterator<Tuner> it = mTuners.iterator();
|
||||||
|
|
||||||
if(tuner != null)
|
Tuner tuner;
|
||||||
|
|
||||||
|
if(preferredTuner != null)
|
||||||
{
|
{
|
||||||
|
tuner = getTuner(preferredTuner);
|
||||||
|
|
||||||
|
if(tuner != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
|
||||||
|
|
||||||
|
if(source != null)
|
||||||
|
{
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
//Fall through to logger below
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mLog.info("Unable to source channel [" + tunerChannel.getFrequency() + "] from preferred tuner [" +
|
||||||
|
preferredTuner + "] - searching for another tuner");
|
||||||
|
}
|
||||||
|
|
||||||
|
while(it.hasNext() && source == null)
|
||||||
|
{
|
||||||
|
tuner = it.next();
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
retVal = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
|
source = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
|
||||||
|
|
||||||
if(retVal != null)
|
|
||||||
{
|
|
||||||
return retVal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
//Fall through to logger below
|
mLog.error("Error obtaining channel from tuner [" + tuner.getName() + "]", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mLog.info("Unable to source channel [" + config.getFrequency() + "] from preferred tuner [" +
|
|
||||||
config.getPreferredTuner() + "] - searching for another tuner");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
while(it.hasNext() && retVal == null)
|
return source;
|
||||||
{
|
|
||||||
tuner = it.next();
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
retVal = tuner.getChannelSourceManager().getSource(tunerChannel, channelSpecification);
|
|
||||||
}
|
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
mLog.error("error obtaining channel from tuner [" + tuner.getName() + "]", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return retVal;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
|
package io.github.dsheirer.source.tuner;
|
||||||
|
|
||||||
|
import io.github.dsheirer.controller.channel.Channel;
|
||||||
|
import io.github.dsheirer.gui.control.MultipleFrequencyEditor;
|
||||||
|
import io.github.dsheirer.gui.editor.Editor;
|
||||||
|
import io.github.dsheirer.source.config.SourceConfigTunerMultipleFrequency;
|
||||||
|
import io.github.dsheirer.source.config.SourceConfiguration;
|
||||||
|
import net.miginfocom.swing.MigLayout;
|
||||||
|
|
||||||
|
import javax.swing.DefaultComboBoxModel;
|
||||||
|
import javax.swing.JComboBox;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.event.TableModelEvent;
|
||||||
|
import javax.swing.event.TableModelListener;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TunerSourceMultipleFrequencyEditor extends Editor<Channel>
|
||||||
|
{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
private static final String NO_PREFERRED_TUNER = "(none)";
|
||||||
|
private static final String UNAVAILABLE_TUNER = " (unavailable)";
|
||||||
|
private MultipleFrequencyEditor mFrequencyEditor = new MultipleFrequencyEditor(0.0, 6000.0);
|
||||||
|
private JComboBox<String> mTunerNameComboBox;
|
||||||
|
private TunerModel mTunerModel;
|
||||||
|
private List<String> mCurrentTunerNames;
|
||||||
|
|
||||||
|
public TunerSourceMultipleFrequencyEditor(TunerModel tunerModel)
|
||||||
|
{
|
||||||
|
mTunerModel = tunerModel;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
loadTunerNames();
|
||||||
|
|
||||||
|
setLayout(new MigLayout("insets 0 0 0 0",
|
||||||
|
"[grow,fill][][right][grow,fill,left]", "[][grow,fill]"));
|
||||||
|
|
||||||
|
mFrequencyEditor.getFrequencyModel().addTableModelListener(new TableModelListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void tableChanged(TableModelEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// mFrequencyEditor.setSize(new Dimension(100, 30));
|
||||||
|
add(mFrequencyEditor, "span 2 2");
|
||||||
|
|
||||||
|
mTunerNameComboBox = new JComboBox<>(mCurrentTunerNames.toArray(new String[mCurrentTunerNames.size()]));
|
||||||
|
mTunerNameComboBox.addActionListener(new ActionListener()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e)
|
||||||
|
{
|
||||||
|
setModified(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
add(new JLabel("Preferred Tuner:"));
|
||||||
|
add(mTunerNameComboBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadTunerNames()
|
||||||
|
{
|
||||||
|
mCurrentTunerNames = new ArrayList<>();
|
||||||
|
|
||||||
|
for(Tuner tuner: mTunerModel.getTuners())
|
||||||
|
{
|
||||||
|
mCurrentTunerNames.add(tuner.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(mCurrentTunerNames);
|
||||||
|
|
||||||
|
mCurrentTunerNames.add(0, NO_PREFERRED_TUNER);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTunerNameCombo(String preferredTuner)
|
||||||
|
{
|
||||||
|
if(preferredTuner != null && !mCurrentTunerNames.contains(preferredTuner))
|
||||||
|
{
|
||||||
|
List<String> updatedTunerNameList = new ArrayList<>(mCurrentTunerNames);
|
||||||
|
String unavailableTuner = preferredTuner + UNAVAILABLE_TUNER;
|
||||||
|
updatedTunerNameList.add(unavailableTuner);
|
||||||
|
String[] tunerNameArray = updatedTunerNameList.toArray(new String[updatedTunerNameList.size()]);
|
||||||
|
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
|
||||||
|
mTunerNameComboBox.setSelectedItem(unavailableTuner);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String[] tunerNameArray = mCurrentTunerNames.toArray(new String[mCurrentTunerNames.size()]);
|
||||||
|
mTunerNameComboBox.setModel(new DefaultComboBoxModel<>(tunerNameArray));
|
||||||
|
|
||||||
|
if(preferredTuner == null)
|
||||||
|
{
|
||||||
|
mTunerNameComboBox.setSelectedItem(NO_PREFERRED_TUNER);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mTunerNameComboBox.setSelectedItem(preferredTuner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void save()
|
||||||
|
{
|
||||||
|
if(hasItem() && isModified())
|
||||||
|
{
|
||||||
|
SourceConfigTunerMultipleFrequency config = new SourceConfigTunerMultipleFrequency();
|
||||||
|
|
||||||
|
String preferredTuner = (String)mTunerNameComboBox.getSelectedItem();
|
||||||
|
|
||||||
|
if(preferredTuner != null)
|
||||||
|
{
|
||||||
|
if(preferredTuner.equalsIgnoreCase(NO_PREFERRED_TUNER))
|
||||||
|
{
|
||||||
|
config.setPreferredTuner(null);
|
||||||
|
}
|
||||||
|
else if(preferredTuner.contains(UNAVAILABLE_TUNER))
|
||||||
|
{
|
||||||
|
//don't do anything - the config already contains this unavailable tuner name
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
config.setPreferredTuner(preferredTuner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.setFrequencies(mFrequencyEditor.getFrequencyModel().getFrequencies());
|
||||||
|
|
||||||
|
getItem().setSourceConfiguration(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setItem(Channel item)
|
||||||
|
{
|
||||||
|
super.setItem(item);
|
||||||
|
|
||||||
|
mFrequencyEditor.getFrequencyModel().clear();
|
||||||
|
|
||||||
|
if(hasItem())
|
||||||
|
{
|
||||||
|
SourceConfiguration config = getItem().getSourceConfiguration();
|
||||||
|
|
||||||
|
if(config instanceof SourceConfigTunerMultipleFrequency)
|
||||||
|
{
|
||||||
|
SourceConfigTunerMultipleFrequency tunerConfig = (SourceConfigTunerMultipleFrequency) config;
|
||||||
|
updateTunerNameCombo(tunerConfig.getPreferredTuner());
|
||||||
|
mFrequencyEditor.getFrequencyModel().setFrequencies(tunerConfig.getFrequencies());
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setModified(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.source.tuner.channel;
|
||||||
|
|
||||||
|
import com.google.common.eventbus.Subscribe;
|
||||||
|
import io.github.dsheirer.channel.state.State;
|
||||||
|
import io.github.dsheirer.eventbus.MyEventBus;
|
||||||
|
import io.github.dsheirer.identifier.Identifier;
|
||||||
|
import io.github.dsheirer.identifier.IdentifierUpdateListener;
|
||||||
|
import io.github.dsheirer.identifier.IdentifierUpdateNotification;
|
||||||
|
import io.github.dsheirer.identifier.decoder.ChannelStateIdentifier;
|
||||||
|
import io.github.dsheirer.module.Module;
|
||||||
|
import io.github.dsheirer.preference.PreferenceType;
|
||||||
|
import io.github.dsheirer.preference.UserPreferences;
|
||||||
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.source.ISourceEventProvider;
|
||||||
|
import io.github.dsheirer.source.SourceEvent;
|
||||||
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.concurrent.ScheduledFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monitors channel state to detect when a channel is not in an identified active state and issues a request to rotate
|
||||||
|
* the next channel frequency in the list.
|
||||||
|
*/
|
||||||
|
public class ChannelRotationMonitor extends Module implements ISourceEventProvider, IdentifierUpdateListener,
|
||||||
|
Listener<IdentifierUpdateNotification>
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(ChannelRotationMonitor.class);
|
||||||
|
|
||||||
|
private UserPreferences mUserPreferences;
|
||||||
|
private Collection<State> mActiveStates;
|
||||||
|
private ScheduledFuture<?> mScheduledFuture;
|
||||||
|
private Listener<SourceEvent> mSourceEventListener;
|
||||||
|
private long mRotationDelay;
|
||||||
|
private boolean mActive;
|
||||||
|
private long mInactiveTimestamp = System.currentTimeMillis();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a channel rotation monitor.
|
||||||
|
*
|
||||||
|
* @param activeStates to monitor
|
||||||
|
* @param userPreferences to receive updates for rotation delay value from user preferences
|
||||||
|
*/
|
||||||
|
public ChannelRotationMonitor(Collection<State> activeStates, UserPreferences userPreferences)
|
||||||
|
{
|
||||||
|
mActiveStates = activeStates;
|
||||||
|
mUserPreferences = userPreferences;
|
||||||
|
mRotationDelay = mUserPreferences.getChannelMultiFrequencyPreference().getRotationDelay();
|
||||||
|
|
||||||
|
//Register to receive user preference updates
|
||||||
|
MyEventBus.getEventBus().register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the external listener to receive frequency rotation requests from this module
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void setSourceEventListener(Listener<SourceEvent> listener)
|
||||||
|
{
|
||||||
|
mSourceEventListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unregisters the external listener from receiving frequency rotation requests.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeSourceEventListener()
|
||||||
|
{
|
||||||
|
mSourceEventListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Listener<IdentifierUpdateNotification> getIdentifierUpdateListener()
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Primary method for receiving channel state identifier notifications, enabling this module to track the
|
||||||
|
* current channel state being provided by the Channel State module.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void receive(IdentifierUpdateNotification identifierUpdateNotification)
|
||||||
|
{
|
||||||
|
Identifier identifier = identifierUpdateNotification.getIdentifier();
|
||||||
|
|
||||||
|
if(identifier instanceof ChannelStateIdentifier)
|
||||||
|
{
|
||||||
|
State currentState = ((ChannelStateIdentifier)identifier).getValue();
|
||||||
|
|
||||||
|
if(mActiveStates.contains(currentState))
|
||||||
|
{
|
||||||
|
mActive = true;
|
||||||
|
}
|
||||||
|
else if(mActive)
|
||||||
|
{
|
||||||
|
mActive = false;
|
||||||
|
mInactiveTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the current active state and when inactive for longer than the specified delay, issues a
|
||||||
|
* channel frequency rotation request
|
||||||
|
*/
|
||||||
|
private void checkState()
|
||||||
|
{
|
||||||
|
if(mSourceEventListener != null && !mActive &&
|
||||||
|
((mInactiveTimestamp + mRotationDelay) < System.currentTimeMillis()))
|
||||||
|
{
|
||||||
|
mSourceEventListener.receive(SourceEvent.frequencyRotationRequest());
|
||||||
|
mInactiveTimestamp = System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guava event bus method to receive notifications when user preferences are updated. This allows us to
|
||||||
|
* dynamically adjust rotation delay if/when the user changes the rotation delay value.
|
||||||
|
*/
|
||||||
|
@Subscribe
|
||||||
|
public void preferenceUpdated(PreferenceType preferenceType)
|
||||||
|
{
|
||||||
|
if(preferenceType == PreferenceType.MULTI_FREQUENCY)
|
||||||
|
{
|
||||||
|
mRotationDelay = mUserPreferences.getChannelMultiFrequencyPreference().getRotationDelay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
if(mScheduledFuture == null)
|
||||||
|
{
|
||||||
|
mScheduledFuture = ThreadPool.SCHEDULED.scheduleAtFixedRate(() -> checkState(), 1, 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
if(mScheduledFuture != null)
|
||||||
|
{
|
||||||
|
mScheduledFuture.cancel(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,315 @@
|
|||||||
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
* *****************************************************************************
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.github.dsheirer.source.tuner.channel;
|
||||||
|
|
||||||
|
import io.github.dsheirer.sample.Listener;
|
||||||
|
import io.github.dsheirer.sample.buffer.ReusableComplexBuffer;
|
||||||
|
import io.github.dsheirer.source.Source;
|
||||||
|
import io.github.dsheirer.source.SourceEvent;
|
||||||
|
import io.github.dsheirer.source.SourceException;
|
||||||
|
import io.github.dsheirer.source.heartbeat.Heartbeat;
|
||||||
|
import io.github.dsheirer.source.tuner.TunerModel;
|
||||||
|
import io.github.dsheirer.util.ThreadPool;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Multiple-frequency tuner channel source. Provides a wrapper around a tuner channel source and listens for external
|
||||||
|
* source events requests to change frequency. Maintains an ordered list of frequencies and automatically tears down
|
||||||
|
* an existing tuner channel source and obtains a new one with the next frequency from the list, on request.
|
||||||
|
*/
|
||||||
|
public class MultiFrequencyTunerChannelSource extends TunerChannelSource
|
||||||
|
{
|
||||||
|
private final static Logger mLog = LoggerFactory.getLogger(MultiFrequencyTunerChannelSource.class);
|
||||||
|
|
||||||
|
private TunerModel mTunerModel;
|
||||||
|
private TunerChannelSource mTunerChannelSource;
|
||||||
|
private List<Long> mFrequencies;
|
||||||
|
private int mFrequencyListPointer = 0;
|
||||||
|
private ChannelSpecification mChannelSpecification;
|
||||||
|
private Listener<ReusableComplexBuffer> mReusableComplexBufferListener;
|
||||||
|
private Listener<Heartbeat> mHeartbeatListener;
|
||||||
|
private String mPreferredTuner;
|
||||||
|
private AtomicBoolean mChangingChannels = new AtomicBoolean();
|
||||||
|
private boolean mStarted;
|
||||||
|
private ConsumerSourceEventAdapter mConsumerSourceEventAdapter = new ConsumerSourceEventAdapter();
|
||||||
|
|
||||||
|
public MultiFrequencyTunerChannelSource(TunerModel tunerModel, TunerChannelSource tunerChannelSource,
|
||||||
|
List<Long> frequencies, ChannelSpecification channelSpecification,
|
||||||
|
String preferredTuner)
|
||||||
|
{
|
||||||
|
super(null, tunerChannelSource.getTunerChannel());
|
||||||
|
mTunerModel = tunerModel;
|
||||||
|
mTunerChannelSource = tunerChannelSource;
|
||||||
|
mTunerChannelSource.setSourceEventListener(mConsumerSourceEventAdapter);
|
||||||
|
mFrequencies = frequencies;
|
||||||
|
mChannelSpecification = channelSpecification;
|
||||||
|
mPreferredTuner = preferredTuner;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cycles this source to use the next frequency in the list
|
||||||
|
*/
|
||||||
|
private void nextFrequency()
|
||||||
|
{
|
||||||
|
if(mChangingChannels.compareAndSet(false, true))
|
||||||
|
{
|
||||||
|
//Only change if we have more than one frequency specified
|
||||||
|
if(mFrequencies.size() > 0)
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
//Shutdown the existing tuner channel source
|
||||||
|
mTunerChannelSource.stop();
|
||||||
|
mTunerChannelSource.removeListener(mReusableComplexBufferListener);
|
||||||
|
mTunerChannelSource.removeSourceEventListener();
|
||||||
|
mTunerChannelSource.removeHeartbeatListener(mHeartbeatListener);
|
||||||
|
mTunerChannelSource.dispose();
|
||||||
|
mTunerChannelSource = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Request the next tuner channel source
|
||||||
|
getNextSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistently attempts to get the next tuner channel source using the next frequency in the list. This method
|
||||||
|
* should only be invoked by the nextFrequency() method that protects access via the mChangingChannels flag.
|
||||||
|
*
|
||||||
|
* Note: if this channel source is shutdown by the external consumer, the mStarted flag will be set to false and
|
||||||
|
* the persistent attempts will stop.
|
||||||
|
*/
|
||||||
|
private void getNextSource()
|
||||||
|
{
|
||||||
|
if(mStarted)
|
||||||
|
{
|
||||||
|
TunerChannel nextChannel = getNextChannel();
|
||||||
|
Source source = mTunerModel.getSource(nextChannel, mChannelSpecification, mPreferredTuner);
|
||||||
|
|
||||||
|
if(source instanceof TunerChannelSource)
|
||||||
|
{
|
||||||
|
mTunerChannelSource = (TunerChannelSource)source;
|
||||||
|
mTunerChannelSource.setSourceEventListener(mConsumerSourceEventAdapter);
|
||||||
|
mTunerChannelSource.setListener(mReusableComplexBufferListener);
|
||||||
|
mTunerChannelSource.addHeartbeatListener(mHeartbeatListener);
|
||||||
|
mTunerChannelSource.start();
|
||||||
|
mChangingChannels.set(false);
|
||||||
|
getSourceEventListener().receive(SourceEvent.frequencyRotationSuccessNotification(this, nextChannel.getFrequency()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//If we don't get a channel source because a tuner is not available or none of the available tuners can
|
||||||
|
//support the frequency, then persistently attempt to get a source by iterating the frequency list
|
||||||
|
if(mTunerChannelSource == null)
|
||||||
|
{
|
||||||
|
getSourceEventListener().receive(SourceEvent.frequencyRotationFailureNotification(this, nextChannel.getFrequency()));
|
||||||
|
ThreadPool.SCHEDULED.schedule(() -> getNextSource(), 1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void start()
|
||||||
|
{
|
||||||
|
//The initial source should not be null
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.start();
|
||||||
|
mStarted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stop()
|
||||||
|
{
|
||||||
|
mStarted = false;
|
||||||
|
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.stop();
|
||||||
|
mTunerChannelSource.removeSourceEventListener();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides the next tuner channel from the frequency list.
|
||||||
|
*/
|
||||||
|
private TunerChannel getNextChannel()
|
||||||
|
{
|
||||||
|
mFrequencyListPointer++;
|
||||||
|
|
||||||
|
if(mFrequencyListPointer >= mFrequencies.size())
|
||||||
|
{
|
||||||
|
mFrequencyListPointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long frequency = mFrequencies.get(mFrequencyListPointer);
|
||||||
|
|
||||||
|
return new TunerChannel(frequency, mChannelSpecification.getBandwidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the heartbeat listener to receive heartbeats from the enclosed tuner channel source.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void addHeartbeatListener(Listener<Heartbeat> listener)
|
||||||
|
{
|
||||||
|
mHeartbeatListener = listener;
|
||||||
|
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.addHeartbeatListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the heartbeat listener from receiving heartbeats from the enclosed tuner channel source
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void removeHeartbeatListener(Listener<Heartbeat> listener)
|
||||||
|
{
|
||||||
|
mHeartbeatListener = null;
|
||||||
|
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.removeHeartbeatListener(listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFrequency(long frequency)
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.setFrequency(frequency);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setSampleRate(double sampleRate)
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.setSampleRate(sampleRate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setChannelFrequencyCorrection(long correction)
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.setChannelFrequencyCorrection(correction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getChannelFrequencyCorrection()
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
return mTunerChannelSource.getChannelFrequencyCorrection();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void processSamples()
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.processSamples();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setListener(Listener<ReusableComplexBuffer> complexBufferListener)
|
||||||
|
{
|
||||||
|
mReusableComplexBufferListener = complexBufferListener;
|
||||||
|
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.setListener(complexBufferListener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeListener(Listener<ReusableComplexBuffer> listener)
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.removeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
mReusableComplexBufferListener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getSampleRate()
|
||||||
|
{
|
||||||
|
if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
return mTunerChannelSource.getSampleRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the parent method so that we can intercept external requests to change to the next frequency in the
|
||||||
|
* frequency list.
|
||||||
|
*
|
||||||
|
* @param sourceEvent to process
|
||||||
|
* @throws SourceException
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void process(SourceEvent sourceEvent) throws SourceException
|
||||||
|
{
|
||||||
|
if(sourceEvent.getEvent() == SourceEvent.Event.REQUEST_FREQUENCY_ROTATION)
|
||||||
|
{
|
||||||
|
nextFrequency();
|
||||||
|
}
|
||||||
|
else if(mTunerChannelSource != null)
|
||||||
|
{
|
||||||
|
mTunerChannelSource.process(sourceEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for source events produced by the enclosed tuner channel source. This adapter receives events from
|
||||||
|
* the wrapped tuner channel source and rebroadcasts them as consumer source events from this enclosing multi-frequency
|
||||||
|
* tuner channel source and to the registered external consumer source event listener.
|
||||||
|
*/
|
||||||
|
public class ConsumerSourceEventAdapter implements Listener<SourceEvent>
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
public void receive(SourceEvent sourceEvent)
|
||||||
|
{
|
||||||
|
broadcastConsumerSourceEvent(sourceEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
|
* ******************************************************************************
|
||||||
* sdrtrunk
|
* sdrtrunk
|
||||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* 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
|
* it under the terms of the GNU General Public License as published by
|
||||||
@@ -14,8 +15,8 @@
|
|||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.source.tuner.channel;
|
package io.github.dsheirer.source.tuner.channel;
|
||||||
|
|
||||||
import io.github.dsheirer.sample.Listener;
|
import io.github.dsheirer.sample.Listener;
|
||||||
@@ -117,7 +118,7 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
|||||||
public void start()
|
public void start()
|
||||||
{
|
{
|
||||||
//Broadcast current frequency and sample rate so consumer can configure correctly
|
//Broadcast current frequency and sample rate so consumer can configure correctly
|
||||||
broadcastConsumerSourceEvent(SourceEvent.frequencyChange(getFrequency()));
|
broadcastConsumerSourceEvent(SourceEvent.frequencyChange(this, getFrequency(), "Startup"));
|
||||||
broadcastProducerSourceEvent(SourceEvent.startSampleStreamRequest(this));
|
broadcastProducerSourceEvent(SourceEvent.startSampleStreamRequest(this));
|
||||||
mScheduledIntervalProcessor.start();
|
mScheduledIntervalProcessor.start();
|
||||||
}
|
}
|
||||||
@@ -186,6 +187,10 @@ public abstract class TunerChannelSource extends ComplexSource implements ISourc
|
|||||||
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE:
|
||||||
setChannelFrequencyCorrection(sourceEvent.getValue().longValue());
|
setChannelFrequencyCorrection(sourceEvent.getValue().longValue());
|
||||||
break;
|
break;
|
||||||
|
case NOTIFICATION_FREQUENCY_ROTATION_FAILURE:
|
||||||
|
case NOTIFICATION_FREQUENCY_ROTATION_SUCCESS:
|
||||||
|
//Ignore
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
mLog.error("Ignoring unrecognized source event: " + sourceEvent.getEvent() + " from [" +
|
mLog.error("Ignoring unrecognized source event: " + sourceEvent.getEvent() + " from [" +
|
||||||
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "") + "]");
|
(sourceEvent.hasSource() ? sourceEvent.getSource().getClass() : "") + "]");
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* sdr-trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014-2018 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
* it under the terms of the GNU General Public License as published by
|
||||||
* later version.
|
* 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
|
* This program is distributed in the hope that it will be useful,
|
||||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
* 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.
|
* You should have received a copy of the GNU General Public License
|
||||||
* If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.source.tuner.frequency;
|
package io.github.dsheirer.source.tuner.frequency;
|
||||||
|
|
||||||
import io.github.dsheirer.source.ISourceEventProcessor;
|
import io.github.dsheirer.source.ISourceEventProcessor;
|
||||||
@@ -267,7 +271,7 @@ public class FrequencyController
|
|||||||
*/
|
*/
|
||||||
protected void broadcastFrequencyChange() throws SourceException
|
protected void broadcastFrequencyChange() throws SourceException
|
||||||
{
|
{
|
||||||
broadcast(SourceEvent.frequencyChange(mFrequency));
|
broadcast(SourceEvent.frequencyChange(null, mFrequency));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,18 +1,22 @@
|
|||||||
/*******************************************************************************
|
/*
|
||||||
* sdr-trunk
|
* ******************************************************************************
|
||||||
* Copyright (C) 2014-2017 Dennis Sheirer
|
* sdrtrunk
|
||||||
|
* Copyright (C) 2014-2019 Dennis Sheirer
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
|
* it under the terms of the GNU General Public License as published by
|
||||||
* later version.
|
* 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
|
* This program is distributed in the hope that it will be useful,
|
||||||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
* 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.
|
* You should have received a copy of the GNU General Public License
|
||||||
* If not, see <http://www.gnu.org/licenses/>
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||||
*
|
* *****************************************************************************
|
||||||
******************************************************************************/
|
*/
|
||||||
package io.github.dsheirer.spectrum;
|
package io.github.dsheirer.spectrum;
|
||||||
|
|
||||||
import com.jidesoft.swing.JideSplitPane;
|
import com.jidesoft.swing.JideSplitPane;
|
||||||
@@ -445,7 +449,7 @@ public class SpectralDisplayPanel extends JPanel implements Listener<ReusableCom
|
|||||||
|
|
||||||
//Fire frequency and sample rate change events so that the spectrum
|
//Fire frequency and sample rate change events so that the spectrum
|
||||||
//and overlay panels can synchronize
|
//and overlay panels can synchronize
|
||||||
process(SourceEvent.frequencyChange(mTuner.getTunerController().getFrequency()));
|
process(SourceEvent.frequencyChange(null, mTuner.getTunerController().getFrequency()));
|
||||||
|
|
||||||
process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate()));
|
process(SourceEvent.sampleRateChange(mTuner.getTunerController().getSampleRate()));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user