From c405ddad96be9e6ad0cd9510f8d4066abf7861d1 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 2 Jan 2016 12:29:40 -0500 Subject: [PATCH 01/74] Work in progress - program compiles and runs --- license/copyright_header.txt | 2 +- src/alias/action/AliasActionManager.java | 11 +- src/audio/AudioModule.java | 4 +- .../ConfigurationControllerModel.java | 64 ++-- src/controller/ControllerPanel.java | 24 +- src/controller/ResourceManager.java | 36 +- src/controller/channel/Channel.java | 343 ++---------------- src/controller/channel/ChannelEditor.java | 6 +- src/controller/channel/ChannelEvent.java | 67 ++-- .../channel/ChannelEventListener.java | 2 +- src/controller/channel/ChannelManager.java | 143 -------- src/controller/channel/ChannelNode.java | 20 +- src/controller/channel/ChannelUtils.java | 8 +- src/controller/site/SiteNode.java | 4 +- src/gui/ChannelMemoryLogger.java | 40 +- src/gui/SDRTrunk.java | 47 ++- src/instrument/gui/DecoderSelectionFrame.java | 10 +- src/instrument/gui/DecoderViewFrame.java | 4 +- src/map/MapPanel.java | 6 +- src/module/decode/DecodeComponentEditor.java | 2 +- src/module/decode/DecoderFactory.java | 8 +- src/module/decode/event/CallEventPanel.java | 6 +- .../decode/event/MessageActivityPanel.java | 6 +- .../decode/p25/audio/P25AudioModule.java | 4 +- src/module/decode/state/ChannelList.java | 4 +- .../decode/state/ChannelStatePanel.java | 4 +- .../decode/state/TrafficChannelManager.java | 19 +- src/playlist/Playlist.java | 24 ++ src/playlist/PlaylistManager.java | 59 ++- src/source/SourceComponentEditor.java | 2 +- src/source/config/SourceConfigMixer.java | 1 + src/source/tuner/TunerNode.java | 8 +- src/spectrum/ChannelSpectrumPanel.java | 6 +- src/spectrum/OverlayPanel.java | 59 ++- src/spectrum/SpectralDisplayPanel.java | 7 +- src/spectrum/SpectrumFrame.java | 8 +- 36 files changed, 374 insertions(+), 694 deletions(-) delete mode 100644 src/controller/channel/ChannelManager.java diff --git a/license/copyright_header.txt b/license/copyright_header.txt index 62c7ab8b..3eaea8b8 100644 --- a/license/copyright_header.txt +++ b/license/copyright_header.txt @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 diff --git a/src/alias/action/AliasActionManager.java b/src/alias/action/AliasActionManager.java index 3451dbdb..d59737f7 100644 --- a/src/alias/action/AliasActionManager.java +++ b/src/alias/action/AliasActionManager.java @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import alias.Alias; -import controller.ResourceManager; +import controller.ThreadPoolManager; /** * Manages all alias action events. Each received message is interrogated for @@ -23,11 +23,11 @@ public class AliasActionManager implements Listener private final static Logger mLog = LoggerFactory.getLogger( AliasActionManager.class ); - private ResourceManager mResourceManager; + private ThreadPoolManager mThreadPoolManager; - public AliasActionManager( ResourceManager resourceManager ) + public AliasActionManager( ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; + mThreadPoolManager = threadPoolManager; } @Override @@ -51,8 +51,7 @@ public class AliasActionManager implements Listener * action needs to setup a timer, and provide the original * message to be used as part of the action (e.g. sending * the message as a text message to a cell phone */ - action.execute( mResourceManager.getThreadPoolManager(), - alias, message ); + action.execute( mThreadPoolManager, alias, message ); } } } diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 572b3b71..7e28b70d 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -183,10 +183,10 @@ public class AudioModule extends Module implements IAudioPacketProvider, { switch( event.getEvent() ) { - case CHANNEL_STATE_RESET: + case NOTIFICATION_STATE_RESET: mAudioMetadata.reset(); break; - case CHANGE_SELECTED: + case NOTIFICATION_SELECTION_CHANGE: mAudioMetadata.setSelected( event.getChannel().isSelected() ); break; default: diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java index 99115842..8bbd48c3 100644 --- a/src/controller/ConfigurationControllerModel.java +++ b/src/controller/ConfigurationControllerModel.java @@ -30,21 +30,14 @@ import javax.swing.tree.TreePath; import map.IconManager; import module.decode.DecoderType; -import module.decode.config.DecodeConfigFactory; import playlist.PlaylistNode; -import source.config.SourceConfigTuner; import source.tuner.Tuner; import source.tuner.TunerGroupNode; import source.tuner.TunerNode; import source.tuner.TunerSelectionListener; import controller.channel.Channel; -import controller.channel.ChannelEvent; -import controller.channel.ChannelEvent.Event; +import controller.channel.ChannelModel; import controller.channel.ChannelNode; -import controller.site.Site; -import controller.system.System; -import controller.system.SystemListNode; -import controller.system.SystemNode; public class ConfigurationControllerModel extends DefaultTreeModel { @@ -62,6 +55,7 @@ public class ConfigurationControllerModel extends DefaultTreeModel // private RecordingGroupNode mRecordingGroupNode; private IconManager mIconManagerFrame; + private ChannelModel mChannelModel; /** * Implements a merged system controller and (tree) model. The user will @@ -69,9 +63,12 @@ public class ConfigurationControllerModel extends DefaultTreeModel * design perspective, the model and the control are necessarily merged to * work well with the JTree as the system view. */ - public ConfigurationControllerModel( ResourceManager resourceManager ) + public ConfigurationControllerModel( ChannelModel channelModel, + ResourceManager resourceManager ) { super( new BaseNode( null ) ); + + mChannelModel = channelModel; mResourceManager = resourceManager; @@ -156,7 +153,7 @@ public class ConfigurationControllerModel extends DefaultTreeModel for( Tuner tuner: tuners ) { - TunerNode child = new TunerNode( tuner ); + TunerNode child = new TunerNode( mChannelModel, tuner ); insertNodeInto( child, parent, parent.getChildCount() ); @@ -173,29 +170,30 @@ public class ConfigurationControllerModel extends DefaultTreeModel */ public void createChannel( long frequency, DecoderType decoder ) { - System system = new System(); - - Site site = new Site(); - system.addSite( site ); - - Channel channel = new Channel(); - channel.setDecodeConfiguration( - DecodeConfigFactory.getDecodeConfiguration( decoder ) ); - SourceConfigTuner sourceConfig = new SourceConfigTuner(); - sourceConfig.setFrequency( frequency ); - channel.setSourceConfiguration( sourceConfig ); - mResourceManager.getChannelManager().channelChanged( new ChannelEvent( channel, Event.CHANNEL_ADDED ) ); - channel.setEnabled( true, true ); - site.addChannel( channel ); - - mPlaylistNode.getPlaylist().getSystemList().addSystem( system ); - - SystemNode systemNode = new SystemNode( system ); - SystemListNode systemListNode = mPlaylistNode.getSystemListNode(); - addNode( systemNode, systemListNode, systemListNode.getChildCount() ); - systemNode.init(); - - systemNode.save(); + throw new UnsupportedOperationException( "This has to be updated to new channel model format" ); +// System system = new System(); +// +// Site site = new Site(); +// system.addSite( site ); +// +// Channel channel = new Channel(); +// channel.setDecodeConfiguration( +// DecodeConfigFactory.getDecodeConfiguration( decoder ) ); +// SourceConfigTuner sourceConfig = new SourceConfigTuner(); +// sourceConfig.setFrequency( frequency ); +// channel.setSourceConfiguration( sourceConfig ); +// mResourceManager.getChannelManager().channelChanged( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) ); +// channel.setEnabled( true, true ); +// site.addChannel( channel ); +// +// mPlaylistNode.getPlaylist().getSystemList().addSystem( system ); +// +// SystemNode systemNode = new SystemNode( system ); +// SystemListNode systemListNode = mPlaylistNode.getSystemListNode(); +// addNode( systemNode, systemListNode, systemListNode.getChildCount() ); +// systemNode.init(); +// +// systemNode.save(); } public void addNode( BaseNode child, BaseNode parent, int index ) diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index cb409dda..64b40a6d 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -42,7 +42,8 @@ import audio.AudioPanel; import com.jidesoft.swing.JideSplitPane; import com.jidesoft.swing.JideTabbedPane; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; public class ControllerPanel extends JPanel { @@ -67,24 +68,25 @@ public class ControllerPanel extends JPanel private AudioPanel mAudioPanel; private MapPanel mMapPanel; - private ChannelManager mChannelManager; + private ChannelModel mChannelModel; private ConfigurationControllerModel mController; protected SettingsManager mSettingsManager; public ControllerPanel( AudioManager audioManager, ConfigurationControllerModel controller, - ChannelManager channelManager, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, MapService mapService, PlaylistManager playlistManager, SettingsManager settingsManager ) { - mChannelManager = channelManager; + mChannelModel = channelModel; mController = controller; mSettingsManager = settingsManager; mAudioPanel = new AudioPanel( mSettingsManager, audioManager ); - mMapPanel = new MapPanel( mapService, mSettingsManager, mChannelManager ); + mMapPanel = new MapPanel( mapService, mSettingsManager, channelProcessingManager ); mCallEventPanel = new CallEventPanel( mSettingsManager ); @@ -120,6 +122,10 @@ public class ControllerPanel extends JPanel mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); mTabbedPane.addTab( "Messages", mMessageActivityPanel ); + + JTable channelsTable = new JTable( mChannelModel ); + JScrollPane channelsScroller = new JScrollPane( channelsTable ); + mTabbedPane.addTab( "Channels", channelsScroller ); /** * Change listener to enable/disable the channel spectrum display @@ -152,10 +158,10 @@ public class ControllerPanel extends JPanel /* Register each of the components to receive channel events when the * channels are selected or change */ - mChannelManager.addListener( mCallEventPanel ); - mChannelManager.addListener( mChannelStateList ); - mChannelManager.addListener( mChannelSpectrumPanel ); - mChannelManager.addListener( mMessageActivityPanel ); + mChannelModel.addListener( mCallEventPanel ); + mChannelModel.addListener( mChannelStateList ); + mChannelModel.addListener( mChannelSpectrumPanel ); + mChannelModel.addListener( mMessageActivityPanel ); JScrollPane channelStateListScroll = new JScrollPane(); channelStateListScroll.getViewport().setView( mChannelStateList ); diff --git a/src/controller/ResourceManager.java b/src/controller/ResourceManager.java index 91256fda..a71de851 100644 --- a/src/controller/ResourceManager.java +++ b/src/controller/ResourceManager.java @@ -26,7 +26,7 @@ import source.SourceManager; import source.recording.RecordingSourceManager; import source.tuner.TunerManager; import audio.AudioManager; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; /** * Manager for system wide resources @@ -34,7 +34,6 @@ import controller.channel.ChannelManager; public class ResourceManager { private AudioManager mAudioManager; - private ChannelManager mChannelManager; private EventLogManager mEventLogManager; private PlaylistManager mPlaylistManager; private RecorderManager mRecorderManager; @@ -46,17 +45,24 @@ public class ResourceManager private MapService mMapService; private ThreadPoolManager mThreadPoolManager; - public ResourceManager() + public ResourceManager( PlaylistManager playlistManager, + ThreadPoolManager threadPoolManager, + ChannelModel channelModel ) { - this( new SettingsManager(), - new PlaylistManager(), - new EventLogManager() ); + this( threadPoolManager, + new SettingsManager(), + playlistManager, + new EventLogManager(), + channelModel ); } - public ResourceManager( SettingsManager settingsManager, + public ResourceManager( ThreadPoolManager threadPoolManager, + SettingsManager settingsManager, PlaylistManager playlistManager, - EventLogManager eventLogManager ) + EventLogManager eventLogManager, + ChannelModel channelModel ) { + mThreadPoolManager = threadPoolManager; mSettingsManager = settingsManager; mPlaylistManager = playlistManager; mEventLogManager = eventLogManager; @@ -65,14 +71,12 @@ public class ResourceManager * ChannelManager requires a reference to the ResourceManager so that * channel processing chains can access system wide resources */ - mThreadPoolManager = new ThreadPoolManager(); mAudioManager = new AudioManager( mThreadPoolManager ); mRecorderManager = new RecorderManager( mThreadPoolManager ); - mChannelManager = new ChannelManager( this ); mSourceManager = new SourceManager( this ); mRecordingSourceManager = new RecordingSourceManager( this ); mTunerManager = new TunerManager( this ); - mController = new ConfigurationControllerModel( this ); + mController = new ConfigurationControllerModel( channelModel, this ); mMapService = new MapService( this ); } @@ -170,14 +174,4 @@ public class ResourceManager { mRecordingSourceManager = manager; } - - public ChannelManager getChannelManager() - { - return mChannelManager; - } - - public void setChannelManager( ChannelManager channelManager ) - { - mChannelManager = channelManager; - } } diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index e23e5d30..3da95791 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -17,12 +17,7 @@ ******************************************************************************/ package controller.channel; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; @@ -49,7 +44,6 @@ import org.slf4j.LoggerFactory; import record.RecorderManager; import record.RecorderType; import record.config.RecordConfiguration; -import sample.Listener; import source.Source; import source.SourceException; import source.SourceType; @@ -63,10 +57,7 @@ import alias.AliasList; import audio.metadata.Metadata; import audio.metadata.MetadataType; import controller.ResourceManager; -import controller.channel.ChannelEvent.Event; import controller.config.Configuration; -import controller.site.Site; -import controller.system.System; import filter.FilterSet; @XmlSeeAlso( { Configuration.class } ) @@ -75,9 +66,7 @@ public class Channel extends Configuration { private final static Logger mLog = LoggerFactory.getLogger( Channel.class ); - private static final boolean ENABLED = true; private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; public enum ChannelType { @@ -85,15 +74,6 @@ public class Channel extends Configuration TRAFFIC }; - - private CopyOnWriteArrayList mChannelListeners = - new CopyOnWriteArrayList(); - - private CopyOnWriteArrayList> mMessageListeners = - new CopyOnWriteArrayList>(); - - private Map mTrafficChannels = new HashMap<>(); - private DecodeConfiguration mDecodeConfiguration = DecodeConfigFactory.getDefaultDecodeConfiguration(); private AuxDecodeConfiguration mAuxDecodeConfiguration = @@ -106,19 +86,19 @@ public class Channel extends Configuration private ResourceManager mResourceManager; private String mAliasListName; - private System mSystem; - private Site mSite; + private String mSystem; + private String mSite; private String mName; private boolean mEnabled; private boolean mSelected; private ChannelType mChannelType; + private ChannelState mChannelState; private CallEventModel mCallEventModel; private MessageActivityModel mMessageActivityModel; private MessageEventLogger mEventLogger; private ProcessingChain mProcessingChain; - private ChannelState mChannelState; private long mTrafficChannelTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; @@ -191,13 +171,12 @@ public class Channel extends Configuration * Note: resource manager instance is applied after object construction * since we construct the channel objects from the xml playlist */ + + //TODO: remove this public void setResourceManager( ResourceManager resourceManager ) { mResourceManager = resourceManager; - /* Add system-wide channel listeners onto this channel */ - addListeners( mResourceManager.getChannelManager().getChannelListeners() ); - /* If we're enabled, fire enable changed event to get processing started */ if( mEnabled ) { @@ -205,6 +184,7 @@ public class Channel extends Configuration } } +//TODO: remove this @XmlTransient public ResourceManager getResourceManager() { @@ -242,8 +222,6 @@ public class Channel extends Configuration public void setSelected( boolean selected ) { mSelected = selected; - - fireChannelEvent( Event.CHANGE_SELECTED ); } @XmlTransient @@ -293,10 +271,7 @@ public class Channel extends Configuration */ public void dispose() { - setEnabled( DISABLED, BROADCAST_CHANGE ); - - /* Broadcast channel deleted event */ - fireChannelEvent( Event.CHANNEL_DELETED ); + setEnabled( DISABLED ); } /** @@ -306,28 +281,8 @@ public class Channel extends Configuration { StringBuilder sb = new StringBuilder(); - if( mSystem != null ) - { - sb.append( mSystem.getName() ); - } - else - { - sb.append( "-" ); - } - - sb.append( "/" ); - - if( mSite != null ) - { - sb.append( mSite.getName() ); - } - else - { - sb.append( "-" ); - } - - sb.append( "/" ); - + sb.append( ( mSystem == null ? "-" : mSystem ) ); + sb.append( ( mSite == null ? "-" : mSite ) ); sb.append( mName ); return sb.toString(); @@ -366,57 +321,41 @@ public class Channel extends Configuration /** * Returns the owning system for this channel. */ - public System getSystem() + @XmlAttribute( name = "system" ) + public String getSystem() { return mSystem; } + + public void setSystem( String system ) + { + mSystem = system; + } public boolean hasSystem() { return mSystem != null; } - /** - * Sets the owning system for this channel and optionally broadcasts a - * system change channel event. - */ - public void setSystem( System system, boolean fireChangeEvent ) - { - mSystem = system; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_SYSTEM ); - } - } - /** * Returns the owning site for this channel. */ - public Site getSite() + @XmlAttribute( name = "site" ) + public String getSite() { return mSite; } + + public void setSite( String site ) + { + mSite = site; + } public boolean hasSite() { return mSite != null; } - /** - * Sets the owning site for this channel and optionally broadcasts a site - * change channel event. - */ - public void setSite( Site site, boolean fireChangeEvent ) - { - mSite = site; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_SITE ); - } - } - /** * Default display string for this channel -- the channel name. */ @@ -444,28 +383,6 @@ public class Channel extends Configuration enableChanged(); } - - /** - * Enables/disables this channel for processing. Automatically starts or - * stops channel processing in response to this change. Optionally - * broadcasts a enabled change channel event. - */ - public void setEnabled( boolean enabled, boolean fireChannelEvent ) - { - setEnabled( enabled ); - - if( fireChannelEvent ) - { - if( enabled ) - { - fireChannelEvent( Event.CHANNEL_ENABLED ); - } - else - { - fireChannelEvent( Event.CHANNEL_DISABLED ); - } - } - } /** * Returns the alias list that is used by this channel for looking up alias @@ -485,22 +402,6 @@ public class Channel extends Configuration { mAliasListName = name; } - - /** - * Sets the alias list to be used for looking up the alias values for the - * various identifiers produced by the decoder. This method is the same - * as setAliasName(name) and optionally broadcasts an alias list change - * channel event. - */ - public void setAliasListName( String name, boolean fireChangeEvent ) - { - mAliasListName = name; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_ALIAS_LIST ); - } - } /** * Gets the decoder configuration used by this channel @@ -536,21 +437,6 @@ public class Channel extends Configuration mAuxDecodeConfiguration = config; } - /** - * Sets the decoder configuration used by this channel and optionally - * broadcasts a decoder change channel event. - */ - public void setDecodeConfiguration( DecodeConfiguration config, - boolean fireChangeEvent ) - { - setDecodeConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_DECODER ); - } - } - /** * Returns the source configuration for this channel. */ @@ -568,21 +454,6 @@ public class Channel extends Configuration mSourceConfiguration = config; } - /** - * Sets the source configuration for this channel and optionally broadcasts - * a source change channel event. - */ - public void setSourceConfiguration( SourceConfiguration config, - boolean fireChangeEvent ) - { - setSourceConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_SOURCE ); - } - } - /** * Returns the event logger configuration for this channel. */ @@ -600,21 +471,6 @@ public class Channel extends Configuration mEventLogConfiguration = config; } - /** - * Sets the event logger configuration for this channel and optionally - * broadcasts an event logger change channel event. - */ - public void setEventLogConfiguration( EventLogConfiguration config, - boolean fireChangeEvent ) - { - setEventLogConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_EVENT_LOGGER ); - } - } - /** * Returns the recorder configuration for this channel. */ @@ -632,21 +488,6 @@ public class Channel extends Configuration mRecordConfiguration = config; } - /** - * Sets the recorder configuration for this channel and optionally broadcasts - * a recorder configuration change channel event. - */ - public void setRecordConfiguration( RecordConfiguration config, - boolean fireChangeEvent ) - { - setRecordConfiguration( config ); - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_RECORDER ); - } - } - /** * Returns the name of this channel. */ @@ -664,20 +505,6 @@ public class Channel extends Configuration mName = name; } - /** - * Sets the name of this channel and optionally broadcasts a name change - * channel event. - */ - public void setName( String name, boolean fireChangeEvent ) - { - mName = name; - - if( fireChangeEvent ) - { - fireChannelEvent( Event.CHANGE_NAME ); - } - } - /** * Responds to changes in the enabled setting by starting the processing * change if the channel is now enabled, or stopping the procesing chain @@ -709,8 +536,6 @@ public class Channel extends Configuration setup(); mProcessingChain.start(); - - fireChannelEvent( Event.CHANNEL_PROCESSING_STARTED ); } catch( SourceException se ) { @@ -837,13 +662,6 @@ public class Channel extends Configuration mProcessingChain.addAudioPacketListener( mResourceManager.getAudioManager() ); - /* Add system-wide message listeners */ - mProcessingChain.addMessageListeners( - mResourceManager.getChannelManager().getMessageListeners() ); - - /* Add individual message listeners */ - mProcessingChain.addMessageListeners( mMessageListeners ); - /* Create the call event model to receive call events */ mCallEventModel = new CallEventModel(); mProcessingChain.addCallEventListener( mCallEventModel ); @@ -855,9 +673,9 @@ public class Channel extends Configuration /* Inject channel metadata that will be inserted into audio packets * for the recorder manager and streaming */ mProcessingChain.broadcast( - new Metadata( MetadataType.SYSTEM, mSystem.getName() ) ); + new Metadata( MetadataType.SYSTEM, mSystem ) ); mProcessingChain.broadcast( - new Metadata( MetadataType.SITE_ID, mSite.getName() ) ); + new Metadata( MetadataType.SITE_ID, mSite ) ); mProcessingChain.broadcast( new Metadata( MetadataType.CHANNEL_NAME, mName ) ); } @@ -914,86 +732,6 @@ public class Channel extends Configuration } } - /** - * Broadcasts a channel change event to all registered listeners - */ - public void fireChannelEvent( Event event ) - { - ChannelEvent channelEvent = new ChannelEvent( this, event ); - - for( ChannelEventListener listener: mChannelListeners ) - { - listener.channelChanged( channelEvent ); - } - - /* Send event to processing chain so modules can respond to channel - * selection events */ - if( mProcessingChain != null ) - { - mProcessingChain.getChannelEventListener().receive( channelEvent ); - } - } - - /** - * Adds a channel listener to receive all channel events from this channel - * and automatically sends the listener a channel add event. - */ - public void addListener( ChannelEventListener listener ) - { - if( !mChannelListeners.contains( listener ) ) - { - mChannelListeners.add( listener ); - - listener.channelChanged( - new ChannelEvent( this, Event.CHANNEL_ADDED ) ); - } - else - { - mLog.error( "Channel - attempt to add already existing channel " - + "listener [" + listener.getClass() + "]" ); - } - } - - /** - * Adds a list of channel listeners to receive all channel events from this - * channel and automatically sends each one a channel add event. - */ - public void addListeners( List listeners ) - { - for( ChannelEventListener listener: listeners ) - { - addListener( listener ); - } - } - - /** - * Removes a channel listener from receiving channel events from this channel - */ - public void removeListener( ChannelEventListener listener ) - { - mChannelListeners.remove( listener ); - } - - public void addListener( Listener listener ) - { - mMessageListeners.add( listener ); - - if( isProcessing() ) - { - mProcessingChain.addMessageListener( listener ); - } - } - - public void removeListener( Listener listener ) - { - mMessageListeners.remove( listener ); - - if( mProcessingChain != null ) - { - mProcessingChain.removeMessageListener( listener ); - } - } - /** * Indicates if any part of this channel is contained within the * minimum and maximum frequency values. @@ -1005,35 +743,4 @@ public class Channel extends Configuration return tunerChannel != null && tunerChannel.overlaps( minimum, maximum ); } - - public void addTrafficChannel( String channelID, Channel channel ) - { - mTrafficChannels.put( channelID, channel ); - } - - public boolean hasTrafficChannel( Channel channel ) - { - return mTrafficChannels.containsValue( channel ); - } - - public void removeTrafficChannel( Channel channel ) - { - String name = channel.getChannelDisplayName(); - - if( mTrafficChannels.containsKey( name ) ) - { - mTrafficChannels.remove( name ); - } - } - - public Collection getTrafficChannels() - { - return Collections.unmodifiableCollection( mTrafficChannels.values() ); - } - - public boolean hasTrafficChannel( String name ) - { - return mTrafficChannels.containsKey( name ); - } - } diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 21306577..c9935088 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -200,14 +200,14 @@ public class ChannelEditor extends Editor implements ActionListener mEventLogEditor.save(); mRecordEditor.save(); - mChannelNode.getChannel().setName( mChannelName.getText(), false ); - mChannelNode.getChannel().setEnabled( mChannelEnabled.isSelected(), false ); + mChannelNode.getChannel().setName( mChannelName.getText() ); + mChannelNode.getChannel().setEnabled( mChannelEnabled.isSelected() ); AliasList selected = mComboAliasLists.getItemAt( mComboAliasLists.getSelectedIndex() ); if( selected != null ) { - mChannelNode.getChannel().setAliasListName( selected.getName(), false ); + mChannelNode.getChannel().setAliasListName( selected.getName() ); } diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java index a8b88eab..93bb94fc 100644 --- a/src/controller/channel/ChannelEvent.java +++ b/src/controller/channel/ChannelEvent.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 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 + ******************************************************************************/ package controller.channel; public class ChannelEvent @@ -27,33 +44,33 @@ public class ChannelEvent } /** - * Channel Events - used to specify channel events and changes to channel - * configurations and settings. + * Channel events to describe the specific event */ public enum Event { - CHANGE_ALIAS_LIST, - CHANGE_DECODER, - CHANGE_EVENT_LOGGER, - CHANGE_NAME, - CHANGE_SELECTED, - CHANGE_SITE, - CHANGE_RECORDER, - CHANGE_SOURCE, - CHANGE_SYSTEM, - - CHANNEL_ADDED, - CHANNEL_DELETED, - - CHANNEL_ENABLED, - CHANNEL_DISABLED, - - CHANNEL_PROCESSING_STARTED, - CHANNEL_PROCESSING_STOPPED, - - CHANNEL_STATE_RESET, - - TRAFFIC_CHANNEL_ADDED, - TRAFFIC_CHANNEL_DELETED; + //Channel is added + NOTIFICATION_ADD, + //Channel configuration has changed + NOTIFICATION_CONFIGURATION_CHANGE, + //Channel is deleted/removed + NOTIFICATION_DELETE, + //Channel enable request was rejected + NOTIFICATION_ENABLE_REJECTED, + //Channel has started processing/decoding + NOTIFICATION_PROCESSING_START, + //Channel has stopped processing/decoding + NOTIFICATION_PROCESSING_STOP, + //Channel's selection state has changed + NOTIFICATION_SELECTION_CHANGE, + //Channel's channel-state has been reset + NOTIFICATION_STATE_RESET, + //Request to deselect the channel + REQUEST_DESELECT, + //Request to disable a channel - response will be a PROCESSING_STOP_NOTIFICATION + REQUEST_DISABLE, + //Request to enable a channel - response will be a PROCESSING_START_NOTIFICATION + REQUEST_ENABLE, + //Request to select the channel + REQUEST_SELECT; } } diff --git a/src/controller/channel/ChannelEventListener.java b/src/controller/channel/ChannelEventListener.java index 7c5405db..ad223435 100644 --- a/src/controller/channel/ChannelEventListener.java +++ b/src/controller/channel/ChannelEventListener.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 diff --git a/src/controller/channel/ChannelManager.java b/src/controller/channel/ChannelManager.java deleted file mode 100644 index 4c594824..00000000 --- a/src/controller/channel/ChannelManager.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ -package controller.channel; - -import java.util.ArrayList; -import java.util.List; - -import message.Message; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import alias.action.AliasActionManager; -import controller.ResourceManager; - -public class ChannelManager implements ChannelEventListener -{ - private final static Logger mLog = LoggerFactory.getLogger( ChannelManager.class ); - - private List mChannels = new ArrayList<>(); - private List mChannelListeners = new ArrayList<>(); - private List> mMessageListeners = new ArrayList<>(); - - private AliasActionManager mAliasActionManager; - - public ChannelManager( ResourceManager resourceManager ) - { - mAliasActionManager = new AliasActionManager( resourceManager ); - - mChannelListeners.add( this ); - - /* Add alias action manager as listener to all channels/messages */ - mMessageListeners.add( mAliasActionManager ); - } - - /** - * Notification that a channel has been added or deleted, so that we can - * keep track of all channels and manage system-wide listeners for each - * of those channels - */ - @SuppressWarnings( "incomplete-switch" ) - @Override - public void channelChanged( ChannelEvent event ) - { - switch( event.getEvent() ) - { - case CHANNEL_ADDED: - if( !mChannels.contains( event.getChannel() ) ) - { - mChannels.add( event.getChannel() ); - } - break; - case CHANNEL_DELETED: - mChannels.remove( event.getChannel() ); - break; - } - } - - /** - * Adds a channel listener that will be added to all channels to receive - * any channel change events. This listener will automatically receive - * a channel add event as it is added to each of the existing channels. - */ - public void addListener( ChannelEventListener listener ) - { - mChannelListeners.add( listener ); - - for( Channel channel: mChannels ) - { - channel.addListener( listener ); - } - } - - public List getChannels() - { - return mChannels; - } - - /** - * Removes a channel listener from being automatically added to all channels - * to receive channel change events. - */ - public void removeListener( ChannelEventListener listener ) - { - mChannelListeners.remove( listener ); - - for( Channel channel: mChannels ) - { - channel.removeListener( listener ); - } - } - - /** - * Returns the list of channel change listeners that will be automatically - * added to all channels to receive channel change events - */ - public List getChannelListeners() - { - return mChannelListeners; - } - - /** - * Returns the list of channel change listeners that will be automatically - * added to all channels to receive channel change events - */ - public List> getMessageListeners() - { - return mMessageListeners; - } - - /** - * Adds a message listener that will be added to all channels to receive - * any messages. - */ - public void addListener( Listener listener ) - { - mMessageListeners.add( listener ); - } - - /** - * Removes a message listener. - */ - public void removeListener( Listener listener ) - { - mMessageListeners.remove( listener ); - } -} diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java index 3c5d70ab..acaa0222 100644 --- a/src/controller/channel/ChannelNode.java +++ b/src/controller/channel/ChannelNode.java @@ -56,18 +56,18 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene if( siteNode != null ) { - getChannel().setSite( siteNode.getSite(), false ); + getChannel().setSite( siteNode.getSite().getName() ); SystemNode systemNode = (SystemNode)siteNode.getParent(); if( systemNode != null ) { - getChannel().setSystem( systemNode.getSystem(), false ); + getChannel().setSystem( systemNode.getSystem().getName() ); } } /* Add this node as listener to receive changes from underlying channel */ - getChannel().addListener( this ); +// getChannel().addListener( this ); /* Add the resource manager to the channel so that the channel * can provide channel change events to all system resources */ @@ -83,19 +83,13 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene switch( event.getEvent() ) { /* Refresh the node for each of these events */ - case CHANGE_DECODER: - case CHANGE_NAME: - case CHANGE_SELECTED: - case CHANGE_SITE: - case CHANGE_SYSTEM: - case CHANNEL_ENABLED: - case CHANNEL_DISABLED: - case CHANNEL_PROCESSING_STARTED: - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_SELECTION_CHANGE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: this.refresh(); break; /* We're being deleted, so cleanup */ - case CHANNEL_DELETED: + case NOTIFICATION_DELETE: delete(); break; default: diff --git a/src/controller/channel/ChannelUtils.java b/src/controller/channel/ChannelUtils.java index 5f50f6e6..f4fd2bf1 100644 --- a/src/controller/channel/ChannelUtils.java +++ b/src/controller/channel/ChannelUtils.java @@ -39,7 +39,7 @@ public class ChannelUtils @Override public void actionPerformed( ActionEvent e ) { - channel.setEnabled( DISABLED, BROADCAST_CHANGE ); + channel.setEnabled( DISABLED ); if( playlistManager != null ) { @@ -82,7 +82,7 @@ public class ChannelUtils @Override public void actionPerformed( ActionEvent e ) { - channel.setEnabled( ENABLED, BROADCAST_CHANGE ); + channel.setEnabled( ENABLED ); if( playlistManager != null ) { @@ -109,10 +109,10 @@ public class ChannelUtils if( response == JOptionPane.YES_OPTION ) { /* Disable the channel */ - channel.setEnabled( DISABLED, BROADCAST_CHANGE ); + channel.setEnabled( DISABLED ); /* Broadcast channel deleted event */ - channel.fireChannelEvent( Event.CHANNEL_DELETED ); +// channel.fireChannelEvent( Event.NOTIFICATION_DELETE ); if( playlistManager != null ) { diff --git a/src/controller/site/SiteNode.java b/src/controller/site/SiteNode.java index 21ec08d8..b1184ab3 100644 --- a/src/controller/site/SiteNode.java +++ b/src/controller/site/SiteNode.java @@ -125,8 +125,8 @@ public class SiteNode extends ConfigurableNode System system = ((SystemNode)getParent()).getSystem(); Channel channel = new Channel(); - channel.setSystem( system, false ); - channel.setSite( getSite(), false ); + channel.setSystem( system.getName() ); + channel.setSite( getSite().getName() ); getSite().addChannel( channel ); diff --git a/src/gui/ChannelMemoryLogger.java b/src/gui/ChannelMemoryLogger.java index 9cf11951..39b1085b 100644 --- a/src/gui/ChannelMemoryLogger.java +++ b/src/gui/ChannelMemoryLogger.java @@ -5,6 +5,7 @@ import java.text.NumberFormat; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; @@ -46,33 +47,40 @@ public class ChannelMemoryLogger implements Runnable, ChannelEventListener { switch( event.getEvent() ) { - case CHANNEL_ADDED: - mChannelCount++; + case NOTIFICATION_ADD: + if( event.getChannel().getChannelType() == ChannelType.TRAFFIC ) + { + mTrafficChannelCount++; + } + else + { + mChannelCount++; + } break; - case CHANNEL_DELETED: - mChannelCount--; + case NOTIFICATION_DELETE: + if( event.getChannel().getChannelType() == ChannelType.TRAFFIC ) + { + mTrafficChannelCount--; + } + else + { + mChannelCount--; + } break; - case CHANNEL_DISABLED: + case REQUEST_DISABLE: break; - case CHANNEL_ENABLED: + case REQUEST_ENABLE: break; - case CHANNEL_PROCESSING_STARTED: + case NOTIFICATION_PROCESSING_START: mChannelProcessingCount++; break; - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_PROCESSING_STOP: mChannelProcessingCount--; break; - case CHANNEL_STATE_RESET: - break; - case TRAFFIC_CHANNEL_ADDED: - mTrafficChannelCount++; - break; - case TRAFFIC_CHANNEL_DELETED: - mTrafficChannelCount--; + case NOTIFICATION_STATE_RESET: break; default: break; - } } } diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 80d3c8fb..79345b07 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -56,6 +56,7 @@ import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import spectrum.SpectralDisplayPanel; import util.TimeStamp; +import alias.action.AliasActionManager; import audio.AudioManager; import com.jidesoft.swing.JideSplitPane; @@ -63,9 +64,12 @@ import com.jidesoft.swing.JideSplitPane; import controller.ConfigurationControllerModel; import controller.ControllerPanel; import controller.ResourceManager; +import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import controller.channel.ChannelEventListener; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.ChannelSelectionManager; public class SDRTrunk { @@ -104,27 +108,48 @@ public class SDRTrunk //Log current properties setting SystemProperties.getInstance().logCurrentSettings(); + ChannelModel channelModel = new ChannelModel(); + + ChannelProcessingManager channelProcessingManager = + new ChannelProcessingManager( channelModel ); + channelModel.addListener( channelProcessingManager ); + + ThreadPoolManager threadPoolManager = new ThreadPoolManager(); + + AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); + channelProcessingManager.addListener( aliasActionManager ); + + ChannelSelectionManager channelSelectionManager = + new ChannelSelectionManager( channelModel ); + channelModel.addListener( channelSelectionManager ); + + /** * Construct the resource manager now, so that it can use the system * properties that were just loaded */ - ResourceManager resource = new ResourceManager(); - //TODO: get rid of resource manager ... below is start of breaking out //the encapsulated pieces from the resource manager + PlaylistManager playlistManager = new PlaylistManager( channelModel ); + ResourceManager resource = + new ResourceManager( playlistManager, threadPoolManager, channelModel ); AudioManager audio = resource.getAudioManager(); - ChannelManager channel = resource.getChannelManager(); - ConfigurationControllerModel controller = resource.getController(); + ConfigurationControllerModel configurationControllerModel = resource.getController(); MapService map = resource.getMapService(); - PlaylistManager playlist = resource.getPlaylistManager(); SettingsManager settings = resource.getSettingsManager(); - mControllerPanel = new ControllerPanel( audio, controller, channel, - map, playlist, settings ); + mControllerPanel = + new ControllerPanel( audio, + configurationControllerModel, + channelModel, + channelProcessingManager, + map, + playlistManager, + settings ); - mSpectralPanel = new SpectralDisplayPanel( channel, controller, - playlist, settings ); + mSpectralPanel = new SpectralDisplayPanel( channelModel, + configurationControllerModel, playlistManager, settings ); mTitle = getTitle(); @@ -136,7 +161,7 @@ public class SDRTrunk if( mLogChannelAndMemoryUsage ) { Runnable cml = new ChannelMemoryLogger(); - resource.getChannelManager().addListener( (ChannelEventListener)cml ); + channelModel.addListener( (ChannelEventListener)cml ); resource.getThreadPoolManager().scheduleFixedRate( ThreadType.DECODER, cml, 5, TimeUnit.SECONDS ); } diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index 3faccf90..b2b6a32d 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -11,15 +11,18 @@ import javax.swing.JInternalFrame; import module.decode.DecodeComponentEditor; import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; import source.IControllableFileSource; import controller.ResourceManager; +import controller.ThreadPoolManager; +import controller.channel.ChannelModel; public class DecoderSelectionFrame extends JInternalFrame { private static final long serialVersionUID = 1L; private DecodeComponentEditor mDecodeEditor = new DecodeComponentEditor( null ); - private ResourceManager mResourceManager = new ResourceManager(); + private ResourceManager mResourceManager; private IControllableFileSource mSource; private JDesktopPane mDesktop; @@ -27,6 +30,11 @@ public class DecoderSelectionFrame extends JInternalFrame public DecoderSelectionFrame( JDesktopPane desktop, IControllableFileSource source ) { + ChannelModel channelModel = new ChannelModel(); + + mResourceManager = new ResourceManager( new PlaylistManager( channelModel ), + new ThreadPoolManager(), channelModel ); + mDesktop = desktop; mSource = source; diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 08738bdb..9cdfe1c5 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -108,8 +108,8 @@ public class DecoderViewFrame extends JInternalFrame recordConfig, auxConfig, aliasList, - new System( "Instrumented" ), - new Site( "Instrumented" ), + "Instrumented", + "Instrumented", "Channel Name" ); mProcessingChain.addModules( modules ); diff --git a/src/map/MapPanel.java b/src/map/MapPanel.java index 9a0a1e5c..e9b66045 100644 --- a/src/map/MapPanel.java +++ b/src/map/MapPanel.java @@ -35,7 +35,7 @@ import org.jdesktop.swingx.mapviewer.TileFactoryInfo; import sample.Listener; import settings.MapViewSetting; import settings.SettingsManager; -import controller.channel.ChannelManager; +import controller.channel.ChannelProcessingManager; public class MapPanel extends JPanel implements PlottableUpdateListener { @@ -48,14 +48,14 @@ public class MapPanel extends JPanel implements PlottableUpdateListener public MapPanel( MapService mapService, SettingsManager settingsManager, - ChannelManager channelManager ) + ChannelProcessingManager channelProcessingManager ) { mSettingsManager = settingsManager; mMapService = mapService; mMapPainter = new PlottableEntityPainter( mSettingsManager ); /* Add Map Service as message listener to receive all messages */ - channelManager.addListener( (Listener)mMapService ); + channelProcessingManager.addListener( (Listener)mMapService ); init(); } diff --git a/src/module/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java index 46092365..bec8de84 100644 --- a/src/module/decode/DecodeComponentEditor.java +++ b/src/module/decode/DecodeComponentEditor.java @@ -156,7 +156,7 @@ public class DecodeComponentEditor extends AbstractChannelEditor //The component that calls save will invoke the change broadcast mChannelNode.getChannel() - .setDecodeConfiguration( mEditor.getConfig(), false ); + .setDecodeConfiguration( mEditor.getConfig() ); } /** diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 2f40e741..2acc06b1 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -135,8 +135,8 @@ public class DecoderFactory RecordConfiguration recordConfig, AuxDecodeConfiguration auxConfig, AliasList aliasList, - System system, - Site site, + String system, + String site, String channelName ) { List modules = getPrimaryModules( channelType, decodeConfig, @@ -176,8 +176,8 @@ public class DecoderFactory DecodeConfiguration decodeConfig, RecordConfiguration recordConfig, AliasList aliasList, - System system, - Site site, + String system, + String site, String channelName, ResourceManager resourceManager ) { diff --git a/src/module/decode/event/CallEventPanel.java b/src/module/decode/event/CallEventPanel.java index c56f53bf..c9ac7ac0 100644 --- a/src/module/decode/event/CallEventPanel.java +++ b/src/module/decode/event/CallEventPanel.java @@ -62,7 +62,7 @@ public class CallEventPanel extends JPanel implements ChannelEventListener @Override public void channelChanged( final ChannelEvent event ) { - if( event.getEvent() == Event.CHANGE_SELECTED && + if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && event.getChannel().isSelected() ) { if( mDisplayedChannel == null || @@ -99,8 +99,8 @@ public class CallEventPanel extends JPanel implements ChannelEventListener } } - else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DISABLED ) + else if( event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || + event.getEvent() == Event.REQUEST_DISABLE ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) diff --git a/src/module/decode/event/MessageActivityPanel.java b/src/module/decode/event/MessageActivityPanel.java index ae026b14..9f84d619 100644 --- a/src/module/decode/event/MessageActivityPanel.java +++ b/src/module/decode/event/MessageActivityPanel.java @@ -82,7 +82,7 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { boolean changed = false; - if( event.getEvent() == Event.CHANGE_SELECTED && + if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && event.getChannel().isSelected() ) { if( mDisplayedChannel == null || @@ -105,8 +105,8 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener changed = true; } } - else if( event.getEvent() == Event.CHANNEL_PROCESSING_STOPPED || - event.getEvent() == Event.CHANNEL_DISABLED ) + else if( event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || + event.getEvent() == Event.REQUEST_DISABLE ) { if( mDisplayedChannel != null && mDisplayedChannel == event.getChannel() ) diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java index 4fa08148..6e5bfdf9 100644 --- a/src/module/decode/p25/audio/P25AudioModule.java +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -232,10 +232,10 @@ public class P25AudioModule extends Module implements Listener, { switch( event.getEvent() ) { - case CHANNEL_STATE_RESET: + case NOTIFICATION_STATE_RESET: mAudioMetadata.reset(); break; - case CHANGE_SELECTED: + case NOTIFICATION_SELECTION_CHANGE: mAudioMetadata.setSelected( event.getChannel().isSelected() ); break; default: diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index 96b07d96..aff58241 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -81,10 +81,10 @@ public class ChannelList extends JPanel implements ChannelEventListener { switch( event.getEvent() ) { - case CHANNEL_PROCESSING_STARTED: + case NOTIFICATION_PROCESSING_START: addChannelPanel( event.getChannel() ); break; - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_PROCESSING_STOP: removeChannelPanel( event.getChannel() ); break; default: diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java index 3ce05a01..0559da06 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelStatePanel.java @@ -111,12 +111,12 @@ public class ChannelStatePanel extends JPanel setLayout( new MigLayout( "insets 1 1 1 1", "[grow,fill]", "[]0[]0[]") ); - mSystemLabel = new JLabel( mChannel.getSystem().getName() ); + mSystemLabel = new JLabel( mChannel.getSystem() ); mSystemLabel.setFont( mFontDetails ); mSystemLabel.setForeground( mColorLabelDetails ); add( mSystemLabel ); - mSiteLabel = new JLabel( mChannel.getSite().getName() ); + mSiteLabel = new JLabel( mChannel.getSite() ); mSiteLabel.setFont( mFontDetails ); mSiteLabel.setForeground( mColorLabelDetails ); add( mSiteLabel ); diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index 34bb2ecc..18d90d74 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -15,7 +15,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import record.config.RecordConfiguration; -import sample.Broadcaster; import sample.Listener; import source.config.SourceConfigTuner; import source.tuner.TunerChannel; @@ -44,8 +43,8 @@ public class TrafficChannelManager extends Module private DecodeConfiguration mDecodeConfiguration; private RecordConfiguration mRecordConfiguration; private ResourceManager mResourceManager; - private System mSystem; - private Site mSite; + private String mSystem; + private String mSite; private String mAliasListName; private long mTrafficChannelTimeout; private CallEvent mPreviousDoNotMonitorCallEvent; @@ -72,8 +71,8 @@ public class TrafficChannelManager extends Module public TrafficChannelManager( ResourceManager resourceManager, DecodeConfiguration decodeConfiguration, RecordConfiguration recordConfiguration, - System system, - Site site, + String system, + String site, String aliasListName, long trafficChannelTimeout, int trafficChannelPoolSize ) @@ -145,8 +144,8 @@ public class TrafficChannelManager extends Module { channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) ); - channel.setSystem( mSystem, false ); - channel.setSite( mSite, false ); + channel.setSystem( mSystem ); + channel.setSite( mSite ); channel.setName( channelNumber ); channel.setEnabled( true ); @@ -160,8 +159,8 @@ public class TrafficChannelManager extends Module { channel.setEnabled( false ); - channel.setSystem( mSystem, false ); - channel.setSite( mSite, false ); + channel.setSystem( mSystem ); + channel.setSite( mSite ); channel.setName( "Traffic" ); } } @@ -399,7 +398,7 @@ public class TrafficChannelManager extends Module mTrafficChannelsInUse.remove( channelNumber ); /* Disable the channel and broadcast a notification */ - channel.setEnabled( false, true ); + channel.setEnabled( false ); } } } diff --git a/src/playlist/Playlist.java b/src/playlist/Playlist.java index 859f3968..0d63153f 100644 --- a/src/playlist/Playlist.java +++ b/src/playlist/Playlist.java @@ -17,15 +17,20 @@ ******************************************************************************/ package playlist; +import java.util.ArrayList; +import java.util.List; + import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import alias.AliasDirectory; +import controller.channel.Channel; import controller.channel.map.ChannelMapList; import controller.system.SystemList; @XmlSeeAlso( { AliasDirectory.class, + Channel.class, ChannelMapList.class, SystemList.class } ) @@ -35,11 +40,23 @@ public class Playlist private AliasDirectory mAliasDirectory = new AliasDirectory(); private ChannelMapList mChannelMapList = new ChannelMapList(); private SystemList mSystemList = new SystemList(); + private List mChannels = new ArrayList<>(); public Playlist() { } + @XmlElement( name = "channel" ) + public List getChannels() + { + return mChannels; + } + + public void setChannels( List channels ) + { + mChannels = channels; + } + @XmlElement( name = "alias_directory" ) public AliasDirectory getAliasDirectory() { @@ -64,6 +81,13 @@ public class Playlist mSystemList = list; } + public boolean hasSystemList() + { + return mSystemList != null && + mSystemList.getSystem() != null && + !mSystemList.getSystem().isEmpty(); + } + @XmlElement( name = "channel_maps" ) public ChannelMapList getChannelMapList() { diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index c3a973b4..f23f7075 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -35,6 +35,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import properties.SystemProperties; +import controller.channel.Channel; +import controller.channel.ChannelModel; +import controller.site.Site; +import controller.system.SystemList; public class PlaylistManager { @@ -43,8 +47,11 @@ public class PlaylistManager private Playlist mPlaylist = new Playlist(); - public PlaylistManager() + private ChannelModel mChannelModel; + + public PlaylistManager( ChannelModel channelModel ) { + mChannelModel = channelModel; init(); } @@ -52,6 +59,15 @@ public class PlaylistManager { return mPlaylist; } + + /** + * Transfers data from persisted playlist into system models + */ + private void transferPlaylistToModels() + { + //Load channels + mChannelModel.addChannels( mPlaylist.getChannels() ); + } /** * Loads playlist from the current playlist file, or the default playlist file, @@ -70,6 +86,8 @@ public class PlaylistManager props.get( "playlist.currentfilename", defaultPlaylistFile ); load( playlistFolder.resolve( playlistFile ) ); + + transferPlaylistToModels(); } @@ -205,5 +223,44 @@ public class PlaylistManager { mPlaylist = new Playlist(); } + + //Check for and convert from legacy play list format + if( mPlaylist.hasSystemList() ) + { + convertPlaylistFormat(); + } + } + + /** + * Converts playlist data over to new format + */ + private void convertPlaylistFormat() + { + mLog.info( "Legacy playlist format detected - converting ..." ); + + //Playlist version 1 to version 2 format conversion. In order to keep + //backwards compatibility, transfer all of the System-Site-Channel + //objects over to the new channel list format + SystemList systemList = mPlaylist.getSystemList(); + + for( controller.system.System system: systemList.getSystem() ) + { + for( Site site: system.getSite() ) + { + for( Channel channel: site.getChannel() ) + { + channel.setSystem( system.getName() ); + channel.setSite( site.getName() ); + + mPlaylist.getChannels().add( channel ); + } + } + } + + mPlaylist.getSystemList().clearSystems(); + + mLog.info( "Converted [" + mPlaylist.getChannels().size() + "] channels to new playlist format" ); + + save(); } } diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java index 0cf4f186..c4d4e4a2 100644 --- a/src/source/SourceComponentEditor.java +++ b/src/source/SourceComponentEditor.java @@ -124,6 +124,6 @@ public class SourceComponentEditor extends AbstractChannelEditor //Let the calling save method send the config change event mChannelNode.getChannel() - .setSourceConfiguration( mEditor.getConfig(), false ); + .setSourceConfiguration( mEditor.getConfig() ); } } diff --git a/src/source/config/SourceConfigMixer.java b/src/source/config/SourceConfigMixer.java index 1074b3cf..01a9e1af 100644 --- a/src/source/config/SourceConfigMixer.java +++ b/src/source/config/SourceConfigMixer.java @@ -42,6 +42,7 @@ public class SourceConfigMixer extends SourceConfiguration { StringBuilder sb = new StringBuilder(); + sb.append( "Mixer: " ); sb.append( mMixer ); sb.append( "-" ); sb.append( mChannel.toString() ); diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java index 9d2f4d99..626cda6b 100644 --- a/src/source/tuner/TunerNode.java +++ b/src/source/tuner/TunerNode.java @@ -34,6 +34,7 @@ import source.tuner.frequency.FrequencyChangeListener; import spectrum.SpectrumFrame; import controller.BaseNode; import controller.ResourceManager; +import controller.channel.ChannelModel; public class TunerNode extends BaseNode implements FrequencyChangeListener { @@ -46,11 +47,14 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener private long mFrequency; private int mSampleRate; + private ChannelModel mChannelModel; - public TunerNode( Tuner tuner ) + public TunerNode( ChannelModel channelModel, Tuner tuner ) { super( tuner ); //does this make it a super tuner? + mChannelModel = channelModel; + tuner.addListener( this ); try @@ -115,7 +119,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener { ResourceManager rm = getModel().getResourceManager(); - SpectrumFrame frame = new SpectrumFrame( rm.getChannelManager(), + SpectrumFrame frame = new SpectrumFrame( mChannelModel, rm.getController(), rm.getPlaylistManager(), rm.getSettingsManager(), getTuner() ); } diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index 96cbefa1..5a87dc4c 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -166,7 +166,7 @@ public class ChannelSpectrumPanel extends JPanel { switch( event.getEvent() ) { - case CHANGE_SELECTED: + case NOTIFICATION_SELECTION_CHANGE: if( !event.getChannel().isSelected() ) { stop(); @@ -192,7 +192,7 @@ public class ChannelSpectrumPanel extends JPanel start(); } break; - case CHANNEL_DISABLED: + case REQUEST_DISABLE: if( event.getChannel() == mCurrentChannel ) { if( mEnabled.get() ) @@ -203,7 +203,7 @@ public class ChannelSpectrumPanel extends JPanel mCurrentChannel = null; } break; - case CHANNEL_ENABLED: + case REQUEST_ENABLE: if( event.getChannel() == mCurrentChannel && !event.getChannel().isProcessing() ) { diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index b22896b5..f51f744d 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -32,7 +32,6 @@ import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.JPanel; @@ -52,7 +51,7 @@ import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; public class OverlayPanel extends JPanel implements ChannelEventListener, @@ -111,14 +110,14 @@ public class OverlayPanel extends JPanel private LabelSizeManager mLabelSizeMonitor = new LabelSizeManager(); private SettingsManager mSettingsManager; - private ChannelManager mChannelManager; + private ChannelModel mChannelModel; /** * Translucent overlay panel for displaying channel configurations, * processing channels, selected channels, frequency labels and lines, and * a cursor with a frequency readout. */ public OverlayPanel( SettingsManager settingsManager, - ChannelManager channelManager ) + ChannelModel channelModel ) { mSettingsManager = settingsManager; @@ -127,11 +126,11 @@ public class OverlayPanel extends JPanel mSettingsManager.addListener( this ); } - mChannelManager = channelManager; + mChannelModel = channelModel; - if( mChannelManager != null ) + if( mChannelModel != null ) { - mChannelManager.addListener( this ); + mChannelModel.addListener( this ); } addComponentListener( mLabelSizeMonitor ); @@ -145,14 +144,13 @@ public class OverlayPanel extends JPanel public void dispose() { - if( mChannelManager != null ) + if( mChannelModel != null ) { - mChannelManager.removeListener( this ); + mChannelModel.removeListener( this ); } - mChannelManager = null; + mChannelModel = null; -// mChannels.clear(); mVisibleChannels.clear(); if( mSettingsManager != null ) @@ -571,7 +569,7 @@ public class OverlayPanel extends JPanel double yAxis = 0; //Draw the system label and adjust the y-axis position - String system = channel.hasSystem() ? channel.getSystem().getName() : " "; + String system = channel.hasSystem() ? channel.getSystem() : " "; yAxis += drawLabel( graphics, system, @@ -581,7 +579,7 @@ public class OverlayPanel extends JPanel width ); //Draw the site label and adjust the y-axis position - String site = channel.hasSite() ? channel.getSite().getName() : " "; + String site = channel.hasSite() ? channel.getSite() : " "; yAxis += drawLabel( graphics, site, @@ -689,20 +687,12 @@ public class OverlayPanel extends JPanel } /** - * Reset the visible channel configs list + * Reset the visible channels list */ mVisibleChannels.clear(); - - long minimum = getMinFrequency(); - long maximum = getMaxFrequency(); - - for( Channel channel: mChannelManager.getChannels() ) - { - if( channel.isWithin( minimum, maximum ) ) - { - mVisibleChannels.add( channel ); - } - } + mVisibleChannels.addAll( + mChannelModel.getChannelsInFrequencyRange( getMinFrequency(), + getMaxFrequency() ) ); } /** @@ -716,27 +706,20 @@ public class OverlayPanel extends JPanel switch( event.getEvent() ) { -// case CHANNEL_ADDED: - case CHANNEL_ENABLED: -// if( !mChannels.contains( channel ) ) -// { -// mChannels.add( channel ); -// } - - if( channel.isWithin( getMinFrequency(), getMaxFrequency() ) && - !mVisibleChannels.contains( channel ) ) + case NOTIFICATION_ADD: + case NOTIFICATION_PROCESSING_START: + if( !mVisibleChannels.contains( channel ) && + channel.isWithin( getMinFrequency(), getMaxFrequency() ) ) { mVisibleChannels.add( channel ); } break; - case CHANNEL_DELETED: -// mChannels.remove( channel ); + case NOTIFICATION_DELETE: mVisibleChannels.remove( channel ); break; - case CHANNEL_PROCESSING_STOPPED: + case NOTIFICATION_PROCESSING_STOP: if( channel.getChannelType() == ChannelType.TRAFFIC ) { -// mChannels.remove( channel ); mVisibleChannels.remove( channel ); } break; diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index d335f2a8..e03d27e9 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -28,7 +28,6 @@ import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.text.DecimalFormat; import java.util.ArrayList; -import java.util.Arrays; import java.util.Hashtable; import javax.swing.JCheckBoxMenuItem; @@ -82,7 +81,7 @@ import com.jidesoft.swing.JideSplitPane; import controller.ConfigurationControllerModel; import controller.channel.Channel; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; import controller.channel.ChannelUtils; import dsp.filter.Window.WindowType; import dsp.filter.smoothing.SmoothingFilter.SmoothingType; @@ -133,7 +132,7 @@ public class SpectralDisplayPanel extends JPanel * the DFT is translated to decibels for display in the spectrum and * waterfall components. */ - public SpectralDisplayPanel( ChannelManager channelManager, + public SpectralDisplayPanel( ChannelModel channelModel, ConfigurationControllerModel controller, PlaylistManager playlistManager, SettingsManager settingsManager ) @@ -143,7 +142,7 @@ public class SpectralDisplayPanel extends JPanel mSettingsManager = settingsManager; mSpectrumPanel = new SpectrumPanel( mSettingsManager ); - mOverlayPanel = new OverlayPanel( mSettingsManager, channelManager ); + mOverlayPanel = new OverlayPanel( mSettingsManager, channelModel ); mWaterfallPanel = new WaterfallPanel( mSettingsManager ); //Register to receive tuner selection events diff --git a/src/spectrum/SpectrumFrame.java b/src/spectrum/SpectrumFrame.java index 2305721c..aa39a2f2 100644 --- a/src/spectrum/SpectrumFrame.java +++ b/src/spectrum/SpectrumFrame.java @@ -23,13 +23,13 @@ import java.awt.event.WindowListener; import javax.swing.JFrame; -import playlist.PlaylistManager; import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; import settings.SettingsManager; import source.tuner.Tuner; import controller.ConfigurationControllerModel; import controller.ResourceManager; -import controller.channel.ChannelManager; +import controller.channel.ChannelModel; public class SpectrumFrame extends JFrame implements WindowListener { @@ -38,7 +38,7 @@ public class SpectrumFrame extends JFrame implements WindowListener private ResourceManager mResourceManager; private SpectralDisplayPanel mSpectralDisplayPanel; - public SpectrumFrame( ChannelManager channelManager, + public SpectrumFrame( ChannelModel channelModel, ConfigurationControllerModel controller, PlaylistManager playlistManager, SettingsManager settingsManager, @@ -50,7 +50,7 @@ public class SpectrumFrame extends JFrame implements WindowListener setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); - mSpectralDisplayPanel = new SpectralDisplayPanel( channelManager, + mSpectralDisplayPanel = new SpectralDisplayPanel( channelModel, controller, playlistManager, settingsManager ); mSpectralDisplayPanel.tunerSelected( tuner ); From 4be63d4b95fbc05f5272a96ca2f8871847032ed7 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 10 Jan 2016 18:02:04 -0500 Subject: [PATCH 02/74] Work in progress. -Started work on new channel editor channel viewer/controller -Removed a number of unused buffer package classes -Moved automatic frequency control to be a module and removed special handling -Added IUnfilteredRealBufferListener & Provider interfaces -Completely removed the ResourceManager class --- .classpath | 5 + images/control_play.png | Bin 0 -> 592 bytes images/control_stop.png | Bin 0 -> 403 bytes images/no_icon.png | Bin 688 -> 743 bytes imports/tablefilter-swing-5.2.1.jar | Bin 0 -> 181090 bytes src/alias/AliasDirectoryNode.java | 20 +- src/alias/AliasEditor.java | 30 +- src/alias/AliasIDNode.java | 7 +- src/alias/AliasListNode.java | 18 +- src/alias/AliasNode.java | 78 +-- src/alias/GroupNode.java | 17 +- src/alias/action/AliasActionNode.java | 11 +- src/alias/action/beep/BeepActionNode.java | 5 +- src/alias/action/clip/ClipActionNode.java | 5 +- src/alias/action/script/ScriptActionNode.java | 5 +- src/alias/id/esn/ESNNode.java | 5 +- src/alias/id/fleetsync/FleetsyncIDNode.java | 5 +- src/alias/id/fleetsync/StatusIDNode.java | 5 +- src/alias/id/lojack/LoJackIDNode.java | 5 +- src/alias/id/mdc/MDC1200IDNode.java | 5 +- src/alias/id/mobileID/MINNode.java | 5 +- src/alias/id/mpt1327/MPT1327IDNode.java | 5 +- src/alias/id/siteID/SiteIDNode.java | 5 +- src/alias/id/talkgroup/TalkgroupIDNode.java | 5 +- src/alias/id/uniqueID/UniqueIDNode.java | 5 +- src/alias/priority/PriorityNode.java | 5 +- src/alias/record/NonRecordableNode.java | 5 +- src/audio/AudioModule.java | 40 +- src/audio/AudioPanel.java | 9 +- src/buffer/BufferOverflowException.java | 33 - src/buffer/BufferOverflowListener.java | 23 - src/buffer/ByteArrayDelayBuffer.java | 60 -- src/buffer/ByteArrayListener.java | 23 - src/buffer/ByteBufferListener.java | 25 - src/buffer/ByteBufferProvider.java | 33 - src/buffer/ComplexByteBuffer.java | 91 --- src/buffer/ComplexSampleBufferAssembler.java | 73 --- src/buffer/DelayBuffer.java | 67 -- .../FloatArrayCircularAveragingBuffer.java | 97 --- src/buffer/FloatBufferAssembler.java | 70 --- src/buffer/FloatCircularAveragingBuffer.java | 110 ---- src/buffer/FloatCircularAveragingBuffer2.java | 68 -- src/buffer/FloatSampleBuffer.java | 66 -- src/buffer/FloatSampleBufferAssembler.java | 72 --- src/buffer/FloatTappedCircularBuffer.java | 86 --- src/buffer/FloatToByteBufferAssembler.java | 69 --- src/buffer/StereoByteBuffer.java | 59 -- src/buffer/StereoByteBufferListener.java | 24 - src/buffer/StereoByteBufferProvider.java | 32 - .../StereoToMonoByteBufferConverter.java | 58 -- src/controller/ConfigurableNode.java | 14 +- .../ConfigurationControllerModel.java | 63 +- src/controller/ControllerPanel.java | 43 +- ...ditor.java => OldConfigurationEditor.java} | 4 +- src/controller/ResourceManager.java | 177 ------ .../channel/AbstractChannelEditor.java | 13 +- src/controller/channel/Channel.java | 584 ++++-------------- .../ChannelConfigurationEditor.java} | 37 +- src/controller/channel/ChannelEditor.java | 284 ++++----- src/controller/channel/ChannelEvent.java | 2 + src/controller/channel/ChannelModel.java | 346 +++++++++++ src/controller/channel/ChannelNode.java | 33 +- .../channel/ChannelProcessingManager.java | 301 +++++++++ .../channel/ChannelSelectionManager.java | 71 +++ src/controller/channel/ChannelUtils.java | 45 +- .../channel/ChannelValidationException.java | 11 - .../ConfigurationValidationException.java | 23 + .../channel/IChannelEventBroadcaster.java} | 14 +- .../channel/NameConfigurationEditor.java | 221 +++++++ src/controller/channel/OldChannelEditor.java | 260 ++++++++ .../channel/map/ChannelMapListNode.java | 9 +- .../channel/map/ChannelMapNode.java | 5 +- src/controller/site/SiteNode.java | 31 +- src/controller/system/SystemListNode.java | 29 +- src/controller/system/SystemNode.java | 26 +- .../filter/dc/DCRemovalFilter_RealBuffer.java | 60 ++ src/dsp/psk/LSMDemodulator.java | 8 +- src/dsp/psk/LSMDemodulator2.java | 8 +- src/gui/SDRTrunk.java | 80 ++- src/gui/control/JFrequencyControl.java | 23 +- .../InstrumentableProcessingChain.java | 2 + src/instrument/gui/DecoderSelectionFrame.java | 15 +- src/instrument/gui/DecoderViewFrame.java | 38 +- src/map/IconManager.java | 26 +- src/map/MapPanel.java | 7 +- src/map/MapService.java | 10 +- src/module/ProcessingChain.java | 209 ++++--- .../decode/AuxDecodeComponentEditor.java | 25 +- src/module/decode/DecodeComponentEditor.java | 60 +- src/module/decode/DecodeEditor.java | 21 +- src/module/decode/DecoderFactory.java | 142 ++--- src/module/decode/event/CallEventPanel.java | 71 ++- .../decode/event/MessageActivityPanel.java | 125 ++-- .../decode/fleetsync2/Fleetsync2Decoder.java | 6 +- src/module/decode/lj1200/LJ1200Decoder.java | 6 +- src/module/decode/ltrnet/LTRNetDecoder.java | 11 +- .../ltrstandard/LTRStandardDecoder.java | 10 +- src/module/decode/mdc1200/MDCDecoder.java | 6 +- .../decode/mpt1327/MPT1327ConfigEditor.java | 73 ++- src/module/decode/mpt1327/MPT1327Decoder.java | 6 +- src/module/decode/p25/C4FMSymbolFilter.java | 110 +++- src/module/decode/p25/P25DecodeEditor.java | 64 +- src/module/decode/p25/P25Decoder.java | 4 +- src/module/decode/p25/P25_C4FMDecoder.java | 70 ++- src/module/decode/p25/P25_LSMDecoder.java | 13 - .../decode/passport/PassportDecoder.java | 13 +- .../decode/state/ChannelCollectionPanel.java | 14 +- src/module/decode/state/ChannelList.java | 14 +- .../decode/state/ChannelStatePanel.java | 49 +- .../decode/state/TrafficChannelManager.java | 92 ++- src/module/decode/tait/Tait1200Decoder.java | 6 +- .../demodulate/am/AMDemodulatorModule.java | 8 +- .../audio/DemodulatedAudioFilterModule.java | 101 +++ .../demodulate/fm/FMDemodulatorModule.java | 122 +--- src/module/log/EventLogComponentEditor.java | 28 +- src/playlist/PlaylistEditor.java | 88 +++ src/playlist/PlaylistManager.java | 74 ++- src/playlist/PlaylistNode.java | 39 +- src/record/RecordComponentEditor.java | 29 +- src/record/RecordEditor.java | 21 +- src/record/RecorderManager.java | 9 +- src/record/wave/RealBufferWaveRecorder.java | 6 +- .../real/IFilteredRealBufferListener.java | 8 + .../real/IFilteredRealBufferProvider.java | 9 + src/sample/real/IRealBufferListener.java | 8 - src/sample/real/IRealBufferProvider.java | 9 - .../real/IUnFilteredRealBufferListener.java | 8 + .../real/IUnFilteredRealBufferProvider.java | 9 + src/settings/SettingsManager.java | 8 +- src/source/ComplexSource.java | 4 +- src/source/EmptySourceEditor.java | 19 +- src/source/FloatSource.java | 12 - src/source/RealSource.java | 4 +- src/source/Source.java | 12 +- src/source/SourceComponentEditor.java | 44 +- src/source/SourceEditor.java | 17 +- src/source/SourceEditorFactory.java | 52 -- src/source/SourceManager.java | 79 ++- src/source/mixer/MixerEditor.java | 25 +- src/source/mixer/MixerManager.java | 17 +- src/source/mixer/RealMixerSource.java | 26 +- src/source/recording/Recording.java | 25 +- src/source/recording/RecordingEditor.java | 27 +- src/source/recording/RecordingGroupNode.java | 28 +- src/source/recording/RecordingNode.java | 7 +- .../recording/RecordingSourceManager.java | 53 +- .../tuner/ITunerChannelProvider.java} | 54 +- src/source/tuner/Tuner.java | 36 +- src/source/tuner/TunerChannelProvider.java | 28 - src/source/tuner/TunerChannelSource.java | 266 ++++---- src/source/tuner/TunerController.java | 15 +- src/source/tuner/TunerEditor.java | 22 +- src/source/tuner/TunerManager.java | 46 +- src/source/tuner/TunerNode.java | 36 +- src/source/tuner/airspy/AirspyTuner.java | 6 +- .../airspy/AirspyTunerConfigurationPanel.java | 30 +- .../tuner/airspy/AirspyTunerEditorPanel.java | 19 +- src/source/tuner/ettus/B100Tuner.java | 6 +- .../tuner/ettus/B100TunerEditorPanel.java | 8 +- src/source/tuner/fcd/FCDTuner.java | 9 +- src/source/tuner/fcd/FCDTunerController.java | 4 +- .../proV1/FCD1TunerConfigurationPanel.java | 26 +- .../tuner/fcd/proV1/FCD1TunerController.java | 6 +- .../tuner/fcd/proV1/FCD1TunerEditorPanel.java | 19 +- .../FCD2TunerConfigurationPanel.java | 26 +- .../fcd/proplusV2/FCD2TunerController.java | 6 +- .../fcd/proplusV2/FCD2TunerEditorPanel.java | 19 +- .../AutomaticFrequencyControl_R.java | 181 ------ .../AutomaticFrequencyControl_RB.java | 197 ------ .../frequency/DCTrackingFrequencyControl.java | 135 ++++ .../frequency/FrequencyChangeBroadcaster.java | 26 - .../tuner/frequency/FrequencyChangeEvent.java | 11 +- .../FrequencyChangeProcessorWrapper.java | 32 + .../tuner/frequency/FrequencyController.java | 24 +- .../frequency/FrequencyCorrectionControl.java | 161 ----- ...ner.java => IFrequencyChangeListener.java} | 6 +- .../frequency/IFrequencyChangeProcessor.java | 6 + .../frequency/IFrequencyChangeProvider.java | 9 + .../IFrequencyCorrectionController.java | 8 - src/source/tuner/hackrf/HackRFTuner.java | 6 +- .../hackrf/HackRFTunerConfigurationPanel.java | 25 +- .../tuner/hackrf/HackRFTunerEditorPanel.java | 20 +- src/source/tuner/rtl/RTL2832Tuner.java | 10 +- .../tuner/rtl/RTL2832TunerController.java | 6 +- .../rtl/e4k/E4KTunerConfigurationPanel.java | 28 +- .../tuner/rtl/e4k/E4KTunerController.java | 6 +- .../tuner/rtl/e4k/E4KTunerEditorPanel.java | 17 +- .../r820t/R820TTunerConfigurationPanel.java | 26 +- .../tuner/rtl/r820t/R820TTunerController.java | 6 +- .../rtl/r820t/R820TTunerEditorPanel.java | 17 +- src/source/wave/ComplexWaveSource.java | 35 +- src/source/wave/RealWaveSource.java | 39 +- src/spectrum/ChannelSpectrumPanel.java | 75 +-- src/spectrum/DFTProcessor.java | 6 +- src/spectrum/OverlayPanel.java | 18 +- src/spectrum/SpectralDisplayPanel.java | 58 +- src/spectrum/SpectrumFrame.java | 12 +- 197 files changed, 4181 insertions(+), 4439 deletions(-) create mode 100644 images/control_play.png create mode 100644 images/control_stop.png create mode 100644 imports/tablefilter-swing-5.2.1.jar delete mode 100644 src/buffer/BufferOverflowException.java delete mode 100644 src/buffer/BufferOverflowListener.java delete mode 100644 src/buffer/ByteArrayDelayBuffer.java delete mode 100644 src/buffer/ByteArrayListener.java delete mode 100644 src/buffer/ByteBufferListener.java delete mode 100644 src/buffer/ByteBufferProvider.java delete mode 100644 src/buffer/ComplexByteBuffer.java delete mode 100644 src/buffer/ComplexSampleBufferAssembler.java delete mode 100644 src/buffer/DelayBuffer.java delete mode 100644 src/buffer/FloatArrayCircularAveragingBuffer.java delete mode 100644 src/buffer/FloatBufferAssembler.java delete mode 100644 src/buffer/FloatCircularAveragingBuffer.java delete mode 100644 src/buffer/FloatCircularAveragingBuffer2.java delete mode 100644 src/buffer/FloatSampleBuffer.java delete mode 100644 src/buffer/FloatSampleBufferAssembler.java delete mode 100644 src/buffer/FloatTappedCircularBuffer.java delete mode 100644 src/buffer/FloatToByteBufferAssembler.java delete mode 100644 src/buffer/StereoByteBuffer.java delete mode 100644 src/buffer/StereoByteBufferListener.java delete mode 100644 src/buffer/StereoByteBufferProvider.java delete mode 100644 src/buffer/StereoToMonoByteBufferConverter.java rename src/controller/{ConfigurationEditor.java => OldConfigurationEditor.java} (95%) delete mode 100644 src/controller/ResourceManager.java rename src/controller/{Editor.java => channel/ChannelConfigurationEditor.java} (58%) create mode 100644 src/controller/channel/ChannelModel.java create mode 100644 src/controller/channel/ChannelProcessingManager.java create mode 100644 src/controller/channel/ChannelSelectionManager.java delete mode 100644 src/controller/channel/ChannelValidationException.java create mode 100644 src/controller/channel/ConfigurationValidationException.java rename src/{buffer/ComplexByteBufferListener.java => controller/channel/IChannelEventBroadcaster.java} (77%) create mode 100644 src/controller/channel/NameConfigurationEditor.java create mode 100644 src/controller/channel/OldChannelEditor.java create mode 100644 src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java create mode 100644 src/module/demodulate/audio/DemodulatedAudioFilterModule.java create mode 100644 src/playlist/PlaylistEditor.java create mode 100644 src/sample/real/IFilteredRealBufferListener.java create mode 100644 src/sample/real/IFilteredRealBufferProvider.java delete mode 100644 src/sample/real/IRealBufferListener.java delete mode 100644 src/sample/real/IRealBufferProvider.java create mode 100644 src/sample/real/IUnFilteredRealBufferListener.java create mode 100644 src/sample/real/IUnFilteredRealBufferProvider.java delete mode 100644 src/source/FloatSource.java delete mode 100644 src/source/SourceEditorFactory.java rename src/{buffer/ShortComplexByteBuffer.java => source/tuner/ITunerChannelProvider.java} (54%) delete mode 100644 src/source/tuner/TunerChannelProvider.java delete mode 100644 src/source/tuner/frequency/AutomaticFrequencyControl_R.java delete mode 100644 src/source/tuner/frequency/AutomaticFrequencyControl_RB.java create mode 100644 src/source/tuner/frequency/DCTrackingFrequencyControl.java delete mode 100644 src/source/tuner/frequency/FrequencyChangeBroadcaster.java create mode 100644 src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java delete mode 100644 src/source/tuner/frequency/FrequencyCorrectionControl.java rename src/source/tuner/frequency/{FrequencyChangeListener.java => IFrequencyChangeListener.java} (90%) create mode 100644 src/source/tuner/frequency/IFrequencyChangeProcessor.java create mode 100644 src/source/tuner/frequency/IFrequencyChangeProvider.java delete mode 100644 src/source/tuner/frequency/IFrequencyCorrectionController.java diff --git a/.classpath b/.classpath index a630c18d..2217d1f1 100644 --- a/.classpath +++ b/.classpath @@ -25,5 +25,10 @@ + + + + + diff --git a/images/control_play.png b/images/control_play.png new file mode 100644 index 0000000000000000000000000000000000000000..0846555d0ca84cb99d4c70dad80144a232604041 GIT binary patch literal 592 zcmV-W0k7R5;6} zlgp~&KoEw{L*wq6jM9+RMU)_iNO6K~b@$|K=nj zb2!5=4Mjq_zrU*f>U2#vSVnMZ9ja4cY`AdOM*t}k^goWqfa3Iq(>2kSH;P81hAqIyBm_{t1>+!KRdtb~{1AK7>C~ zD-Nov`UX!X6ET_La7f{B_|*cRuZGR%^C`gjd@jmW6h*+;8;{2{8ja|Fzf-YTq+l@k zGLg?!DijI~9$+CG0P)O;^z5vZ zY!uIB*x&E}vNJj4{?GTJBigE^o7UKdzE#&EBXnfjM2N9qUNJ=7T*(!I*v$dVF@wV! zPcbfCO)dpCHwm6#49koVc}1IZ;f0opGWdxBx;Rl@XzG}46S&UgQ6wI6lQE987w+r= zQ{sp)?}bM^P?P)rx$R<IWI`!e`47o+X|cM zhswMI)6H-!+|Ry*GtmT2KZBZTgf*vuh@Iv~E3-o`X2T^+DyW5TGfmLUU@4QG+c^0c z#zg&+`J#c@0((YV&ur`*Iawk7JMET#>EkfEJtz9cD4oNF6e!0Aw?I&|=qKwHAy9XoQ zA^YBJr@&gw)O7(`yrYQGx{Yirh2eMScnk9(q^&}_PQ*$`FTOgh0*()5P;Djfw78dp Z{0&Uoz&aZx@__&V002ovPDHLkV1iM?Q(XW6 literal 688 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

7X;Vp{~T^h)odJa)sXl-E=^62 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

Vi?P8EgPo`UsT2h^tGuEo;vUbNci+z`!vJ1_ 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 literal 0 HcmV?d00001 diff --git a/src/alias/AliasDirectoryNode.java b/src/alias/AliasDirectoryNode.java index a616b755..c44d32b9 100644 --- a/src/alias/AliasDirectoryNode.java +++ b/src/alias/AliasDirectoryNode.java @@ -23,15 +23,27 @@ import java.awt.event.ActionListener; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; +import settings.SettingsManager; import controller.ConfigurableNode; public class AliasDirectoryNode extends ConfigurableNode { private static final long serialVersionUID = 1L; + + private PlaylistManager mPlaylistManager; - public AliasDirectoryNode( AliasDirectory directory ) + private SettingsManager mSettingsManager; + + public AliasDirectoryNode( PlaylistManager playlistManager, + SettingsManager settingsManager, + AliasDirectory directory ) { - super( directory ); + super( playlistManager, directory ); + + mPlaylistManager = playlistManager; + + mSettingsManager = settingsManager; } public AliasDirectory getAliasDirectory() @@ -43,7 +55,7 @@ public class AliasDirectoryNode extends ConfigurableNode { for( AliasList list: getAliasDirectory().getAliasList() ) { - AliasListNode node = new AliasListNode( list ); + AliasListNode node = new AliasListNode( mPlaylistManager, mSettingsManager, list ); getModel().addNode( node, AliasDirectoryNode.this, getChildCount() ); @@ -72,7 +84,7 @@ public class AliasDirectoryNode extends ConfigurableNode getAliasDirectory().addAliasList( list ); - AliasListNode node = new AliasListNode( list ); + AliasListNode node = new AliasListNode( mPlaylistManager, mSettingsManager, list ); getModel().addNode( node, AliasDirectoryNode.this, diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 77be321b..cb4f9682 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -19,6 +19,7 @@ package alias; import java.awt.Color; import java.awt.Dimension; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -32,19 +33,19 @@ import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.tree.TreePath; +import map.IconManager; import map.MapIcon; import map.MapIconListCellRenderer; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import controller.ConfigurableNode; -import controller.ResourceManager; public class AliasEditor extends JPanel implements ActionListener { private static final long serialVersionUID = 1L; private AliasNode mAliasNode; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private JLabel mLabelName; private JTextField mTextAlias; @@ -54,10 +55,11 @@ public class AliasEditor extends JPanel implements ActionListener private JComboBox mComboMapIcon; private JButton mBtnIconManager; - public AliasEditor( AliasNode aliasNode, ResourceManager resourceManager ) + public AliasEditor( AliasNode aliasNode, + SettingsManager settingsManager ) { mAliasNode = aliasNode; - mResourceManager = resourceManager; + mSettingsManager = settingsManager; initGUI(); } @@ -98,8 +100,7 @@ public class AliasEditor extends JPanel implements ActionListener add( new JLabel( "Icon:" ) ); - mComboMapIcon = new JComboBox( - mResourceManager.getSettingsManager().getMapIcons() ); + mComboMapIcon = new JComboBox( mSettingsManager.getMapIcons() ); MapIconListCellRenderer renderer = new MapIconListCellRenderer(); renderer.setPreferredSize( new Dimension( 200, 30 ) ); @@ -112,8 +113,7 @@ public class AliasEditor extends JPanel implements ActionListener iconName = SettingsManager.DEFAULT_ICON; } - MapIcon savedIcon = mResourceManager.getSettingsManager() - .getMapIcon( iconName ); + MapIcon savedIcon = mSettingsManager.getMapIcon( iconName ); if( savedIcon != null ) { @@ -131,7 +131,17 @@ public class AliasEditor extends JPanel implements ActionListener @Override public void actionPerformed( ActionEvent arg0 ) { - mResourceManager.getController().showIconManager(); + final IconManager iconManager = + new IconManager( mSettingsManager, AliasEditor.this ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); } } ); add( mBtnIconManager, "span 2,wrap" ); @@ -194,7 +204,7 @@ public class AliasEditor extends JPanel implements ActionListener mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() ); mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() ); - MapIcon savedIcon = mResourceManager.getSettingsManager() + MapIcon savedIcon = mSettingsManager .getMapIcon( mAliasNode.getAlias().getIconName() ); mComboMapIcon.setSelectedItem( savedIcon ); diff --git a/src/alias/AliasIDNode.java b/src/alias/AliasIDNode.java index c861c56a..3852b44d 100644 --- a/src/alias/AliasIDNode.java +++ b/src/alias/AliasIDNode.java @@ -17,19 +17,20 @@ ******************************************************************************/ package alias; +import playlist.PlaylistManager; import controller.ConfigurableNode; public class AliasIDNode extends ConfigurableNode { private static final long serialVersionUID = 1L; - public AliasIDNode( AliasID id ) + public AliasIDNode( PlaylistManager playlistManager, AliasID id ) { - super( id ); + super( playlistManager, id ); } public void save() { - getModel().getResourceManager().getPlaylistManager().save(); + getPlaylistManager().save(); } } diff --git a/src/alias/AliasListNode.java b/src/alias/AliasListNode.java index 65632971..573708ce 100644 --- a/src/alias/AliasListNode.java +++ b/src/alias/AliasListNode.java @@ -26,15 +26,25 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; +import playlist.PlaylistManager; +import settings.SettingsManager; import controller.ConfigurableNode; public class AliasListNode extends ConfigurableNode { private static final long serialVersionUID = 1L; + + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; - public AliasListNode( AliasList list ) + public AliasListNode( PlaylistManager playlistManager, + SettingsManager settingsManager, + AliasList list ) { - super( list ); + super( playlistManager, list ); + + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; } @Override @@ -52,7 +62,7 @@ public class AliasListNode extends ConfigurableNode { for( Group group: getList().getGroup() ) { - GroupNode node = new GroupNode( group ); + GroupNode node = new GroupNode( mPlaylistManager, mSettingsManager, group ); getModel().addNode( node, AliasListNode.this, getChildCount() ); @@ -81,7 +91,7 @@ public class AliasListNode extends ConfigurableNode getList().addGroup( group ); - GroupNode node = new GroupNode( group ); + GroupNode node = new GroupNode( mPlaylistManager, mSettingsManager, group ); getModel().addNode( node, AliasListNode.this, diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java index 43de7703..070e3b60 100644 --- a/src/alias/AliasNode.java +++ b/src/alias/AliasNode.java @@ -27,6 +27,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import map.MapIcon; +import playlist.PlaylistManager; +import settings.SettingsManager; import alias.action.AliasAction; import alias.action.beep.BeepAction; import alias.action.beep.BeepActionNode; @@ -64,15 +66,22 @@ public class AliasNode extends ConfigurableNode { private static final long serialVersionUID = 1L; - public AliasNode( Alias alias ) + private SettingsManager mSettingsManager; + private PlaylistManager mPlaylistManager; + + public AliasNode( PlaylistManager playlistManager, + SettingsManager settingsManager, + Alias alias ) { - super( alias ); + super( playlistManager, alias ); + + mSettingsManager = settingsManager; } @Override public JPanel getEditor() { - return new AliasEditor( this, getModel().getResourceManager() ); + return new AliasEditor( this, mSettingsManager ); } public Alias getAlias() @@ -84,8 +93,7 @@ public class AliasNode extends ConfigurableNode { String icon = getAlias().getIconName(); - MapIcon mapIcon = getModel().getResourceManager() - .getSettingsManager().getMapIcon( icon ); + MapIcon mapIcon = mSettingsManager.getMapIcon( icon ); if( mapIcon != null ) { @@ -104,51 +112,51 @@ public class AliasNode extends ConfigurableNode switch( aliasID.getType() ) { case ESN: - getModel().addNode( new ESNNode( (Esn)aliasID ), + getModel().addNode( new ESNNode( mPlaylistManager, (Esn)aliasID ), AliasNode.this, getChildCount() ); break; case Fleetsync: - getModel().addNode( new FleetsyncIDNode( (FleetsyncID)aliasID ), + getModel().addNode( new FleetsyncIDNode( mPlaylistManager, (FleetsyncID)aliasID ), AliasNode.this, getChildCount() ); break; case LoJack: - getModel().addNode( new LoJackIDNode( (LoJackFunctionAndID)aliasID ), + getModel().addNode( new LoJackIDNode( mPlaylistManager, (LoJackFunctionAndID)aliasID ), AliasNode.this, getChildCount() ); break; case LTRNetUID: - getModel().addNode( new UniqueIDNode( (UniqueID)aliasID ), + getModel().addNode( new UniqueIDNode( mPlaylistManager, (UniqueID)aliasID ), AliasNode.this, getChildCount() ); break; case MDC1200: - getModel().addNode( new MDC1200IDNode( (MDC1200ID)aliasID ), + getModel().addNode( new MDC1200IDNode( mPlaylistManager, (MDC1200ID)aliasID ), AliasNode.this, getChildCount() ); break; case MIN: - getModel().addNode( new MINNode( (Min)aliasID ), + getModel().addNode( new MINNode( mPlaylistManager, (Min)aliasID ), AliasNode.this, getChildCount() ); break; case MPT1327: - getModel().addNode( new MPT1327IDNode( (MPT1327ID)aliasID ), + getModel().addNode( new MPT1327IDNode( mPlaylistManager, (MPT1327ID)aliasID ), AliasNode.this, getChildCount() ); break; case NonRecordable: - getModel().addNode( new NonRecordableNode( (NonRecordable)aliasID ), + getModel().addNode( new NonRecordableNode( mPlaylistManager, (NonRecordable)aliasID ), AliasNode.this, getChildCount() ); break; case Priority: - getModel().addNode( new PriorityNode( (Priority)aliasID ), + getModel().addNode( new PriorityNode( mPlaylistManager, (Priority)aliasID ), AliasNode.this, getChildCount() ); break; case Site: - getModel().addNode( new SiteIDNode( (SiteID)aliasID ), + getModel().addNode( new SiteIDNode( mPlaylistManager, (SiteID)aliasID ), AliasNode.this, getChildCount() ); break; case Status: - getModel().addNode( new StatusIDNode( (StatusID)aliasID ), + getModel().addNode( new StatusIDNode( mPlaylistManager, (StatusID)aliasID ), AliasNode.this, getChildCount() ); break; case Talkgroup: - getModel().addNode( new TalkgroupIDNode( (TalkgroupID)aliasID ), + getModel().addNode( new TalkgroupIDNode( mPlaylistManager, (TalkgroupID)aliasID ), AliasNode.this, getChildCount() ); break; default: @@ -161,17 +169,17 @@ public class AliasNode extends ConfigurableNode { if( action instanceof BeepAction ) { - getModel().addNode( new BeepActionNode( (BeepAction)action ), + getModel().addNode( new BeepActionNode( mPlaylistManager, (BeepAction)action ), AliasNode.this, getChildCount() ); } else if( action instanceof ClipAction ) { - getModel().addNode( new ClipActionNode( (ClipAction)action ), + getModel().addNode( new ClipActionNode( mPlaylistManager, (ClipAction)action ), AliasNode.this, getChildCount() ); } else if( action instanceof ScriptAction ) { - getModel().addNode( new ScriptActionNode( (ScriptAction)action ), + getModel().addNode( new ScriptActionNode( mPlaylistManager, (ScriptAction)action ), AliasNode.this, getChildCount() ); } } @@ -200,7 +208,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( esn ); - ESNNode node = new ESNNode( esn ); + ESNNode node = new ESNNode( mPlaylistManager, esn ); getModel().addNode( node, AliasNode.this, @@ -221,7 +229,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( fs ); - FleetsyncIDNode node = new FleetsyncIDNode( fs ); + FleetsyncIDNode node = new FleetsyncIDNode( mPlaylistManager, fs ); getModel().addNode( node, AliasNode.this, @@ -242,7 +250,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( lj ); - LoJackIDNode node = new LoJackIDNode( lj ); + LoJackIDNode node = new LoJackIDNode( mPlaylistManager, lj ); getModel().addNode( node, AliasNode.this, @@ -263,7 +271,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( mdc ); - MDC1200IDNode node = new MDC1200IDNode( mdc ); + MDC1200IDNode node = new MDC1200IDNode( mPlaylistManager, mdc ); getModel().addNode( node, AliasNode.this, @@ -284,7 +292,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( min ); - MINNode node = new MINNode( min ); + MINNode node = new MINNode( mPlaylistManager, min ); getModel().addNode( node, AliasNode.this, @@ -305,7 +313,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( mpt ); - MPT1327IDNode node = new MPT1327IDNode( mpt ); + MPT1327IDNode node = new MPT1327IDNode( mPlaylistManager, mpt ); getModel().addNode( node, AliasNode.this, @@ -326,7 +334,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( siteID ); - SiteIDNode node = new SiteIDNode( siteID ); + SiteIDNode node = new SiteIDNode( mPlaylistManager, siteID ); getModel().addNode( node, AliasNode.this, @@ -347,7 +355,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( statusID ); - StatusIDNode node = new StatusIDNode( statusID ); + StatusIDNode node = new StatusIDNode( mPlaylistManager, statusID ); getModel().addNode( node, AliasNode.this, @@ -369,7 +377,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( tg ); - TalkgroupIDNode node = new TalkgroupIDNode( tg ); + TalkgroupIDNode node = new TalkgroupIDNode( mPlaylistManager, tg ); getModel().addNode( node, AliasNode.this, @@ -390,7 +398,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( uid ); - UniqueIDNode node = new UniqueIDNode( uid ); + UniqueIDNode node = new UniqueIDNode( mPlaylistManager, uid ); getModel().addNode( node, AliasNode.this, @@ -416,7 +424,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasAction( clipAction ); - ClipActionNode node = new ClipActionNode( clipAction ); + ClipActionNode node = new ClipActionNode( mPlaylistManager, clipAction ); getModel().addNode( node, AliasNode.this, @@ -439,7 +447,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasAction( beepAction ); - BeepActionNode node = new BeepActionNode( beepAction ); + BeepActionNode node = new BeepActionNode( mPlaylistManager, beepAction ); getModel().addNode( node, AliasNode.this, @@ -462,7 +470,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasAction( scriptAction ); - ScriptActionNode node = new ScriptActionNode( scriptAction ); + ScriptActionNode node = new ScriptActionNode( mPlaylistManager, scriptAction ); getModel().addNode( node, AliasNode.this, @@ -491,7 +499,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( non ); - NonRecordableNode node = new NonRecordableNode( non ); + NonRecordableNode node = new NonRecordableNode( mPlaylistManager, non ); getModel().addNode( node, AliasNode.this, @@ -518,7 +526,7 @@ public class AliasNode extends ConfigurableNode getAlias().addAliasID( priority ); - PriorityNode node = new PriorityNode( priority ); + PriorityNode node = new PriorityNode( mPlaylistManager, priority ); getModel().addNode( node, AliasNode.this, diff --git a/src/alias/GroupNode.java b/src/alias/GroupNode.java index ac0ca894..f8d52904 100644 --- a/src/alias/GroupNode.java +++ b/src/alias/GroupNode.java @@ -26,15 +26,24 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; +import playlist.PlaylistManager; +import settings.SettingsManager; import controller.ConfigurableNode; public class GroupNode extends ConfigurableNode { private static final long serialVersionUID = 1L; + + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; - public GroupNode( Group group ) + public GroupNode( PlaylistManager playlistManager, + SettingsManager settingsManager, + Group group ) { - super( group ); + super( playlistManager, group ); + + mSettingsManager = settingsManager; } @Override @@ -52,7 +61,7 @@ public class GroupNode extends ConfigurableNode { for( Alias alias: getGroup().getAlias() ) { - AliasNode node = new AliasNode( alias ); + AliasNode node = new AliasNode( mPlaylistManager, mSettingsManager, alias ); getModel().addNode( node, GroupNode.this, getChildCount() ); @@ -81,7 +90,7 @@ public class GroupNode extends ConfigurableNode getGroup().addAlias( alias ); - AliasNode node = new AliasNode( alias ); + AliasNode node = new AliasNode( mPlaylistManager, mSettingsManager, alias ); getModel().addNode( node, GroupNode.this, diff --git a/src/alias/action/AliasActionNode.java b/src/alias/action/AliasActionNode.java index 79ee2484..173ecf07 100644 --- a/src/alias/action/AliasActionNode.java +++ b/src/alias/action/AliasActionNode.java @@ -17,20 +17,25 @@ ******************************************************************************/ package alias.action; +import playlist.PlaylistManager; import controller.ConfigurableNode; public class AliasActionNode extends ConfigurableNode { private static final long serialVersionUID = 1L; + + private PlaylistManager mPlaylistManager; - public AliasActionNode( AliasAction action ) + public AliasActionNode( PlaylistManager playlistManager, AliasAction action ) { - super( action ); + super( playlistManager, action ); + + mPlaylistManager = playlistManager; } public void save() { - getModel().getResourceManager().getPlaylistManager().save(); + mPlaylistManager.save(); } public String getIconPath() diff --git a/src/alias/action/beep/BeepActionNode.java b/src/alias/action/beep/BeepActionNode.java index 817e3949..ba5fccdf 100644 --- a/src/alias/action/beep/BeepActionNode.java +++ b/src/alias/action/beep/BeepActionNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasNode; import alias.action.AliasActionNode; @@ -32,9 +33,9 @@ public class BeepActionNode extends AliasActionNode { private static final long serialVersionUID = 1L; - public BeepActionNode( BeepAction beepAction ) + public BeepActionNode( PlaylistManager playlistManager, BeepAction beepAction ) { - super( beepAction ); + super( playlistManager, beepAction ); } @Override diff --git a/src/alias/action/clip/ClipActionNode.java b/src/alias/action/clip/ClipActionNode.java index e3255cd0..15f7005b 100644 --- a/src/alias/action/clip/ClipActionNode.java +++ b/src/alias/action/clip/ClipActionNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasNode; import alias.action.AliasActionNode; @@ -32,9 +33,9 @@ public class ClipActionNode extends AliasActionNode { private static final long serialVersionUID = 1L; - public ClipActionNode( ClipAction clipAction ) + public ClipActionNode( PlaylistManager playlistManager, ClipAction clipAction ) { - super( clipAction ); + super( playlistManager, clipAction ); } @Override diff --git a/src/alias/action/script/ScriptActionNode.java b/src/alias/action/script/ScriptActionNode.java index b0385e3f..cbe25b45 100644 --- a/src/alias/action/script/ScriptActionNode.java +++ b/src/alias/action/script/ScriptActionNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasNode; import alias.action.AliasActionNode; @@ -32,9 +33,9 @@ public class ScriptActionNode extends AliasActionNode { private static final long serialVersionUID = 1L; - public ScriptActionNode( ScriptAction scriptAction ) + public ScriptActionNode( PlaylistManager playlistManager, ScriptAction scriptAction ) { - super( scriptAction ); + super( playlistManager, scriptAction ); } @Override diff --git a/src/alias/id/esn/ESNNode.java b/src/alias/id/esn/ESNNode.java index 1b6234d0..ecdd33e8 100644 --- a/src/alias/id/esn/ESNNode.java +++ b/src/alias/id/esn/ESNNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class ESNNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public ESNNode( Esn esn ) + public ESNNode( PlaylistManager playlistManager, Esn esn ) { - super( esn ); + super( playlistManager, esn ); } @Override diff --git a/src/alias/id/fleetsync/FleetsyncIDNode.java b/src/alias/id/fleetsync/FleetsyncIDNode.java index 9a16c1d9..4d0e09f1 100644 --- a/src/alias/id/fleetsync/FleetsyncIDNode.java +++ b/src/alias/id/fleetsync/FleetsyncIDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class FleetsyncIDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public FleetsyncIDNode( FleetsyncID id ) + public FleetsyncIDNode( PlaylistManager playlistManager, FleetsyncID id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/alias/id/fleetsync/StatusIDNode.java b/src/alias/id/fleetsync/StatusIDNode.java index 621626fb..49aaeb83 100644 --- a/src/alias/id/fleetsync/StatusIDNode.java +++ b/src/alias/id/fleetsync/StatusIDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class StatusIDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public StatusIDNode( StatusID id ) + public StatusIDNode( PlaylistManager playlistManager, StatusID id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/alias/id/lojack/LoJackIDNode.java b/src/alias/id/lojack/LoJackIDNode.java index 3b4f3ed0..fcda77bd 100644 --- a/src/alias/id/lojack/LoJackIDNode.java +++ b/src/alias/id/lojack/LoJackIDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class LoJackIDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public LoJackIDNode( LoJackFunctionAndID id ) + public LoJackIDNode( PlaylistManager playlistManager, LoJackFunctionAndID id ) { - super( id ); + super( playlistManager,id ); } @Override diff --git a/src/alias/id/mdc/MDC1200IDNode.java b/src/alias/id/mdc/MDC1200IDNode.java index 5a5a837b..9c315a49 100644 --- a/src/alias/id/mdc/MDC1200IDNode.java +++ b/src/alias/id/mdc/MDC1200IDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class MDC1200IDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public MDC1200IDNode( MDC1200ID id ) + public MDC1200IDNode( PlaylistManager playlistManager, MDC1200ID id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/alias/id/mobileID/MINNode.java b/src/alias/id/mobileID/MINNode.java index cf832f91..be0b7e83 100644 --- a/src/alias/id/mobileID/MINNode.java +++ b/src/alias/id/mobileID/MINNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class MINNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public MINNode( Min min ) + public MINNode( PlaylistManager playlistManager, Min min ) { - super( min ); + super( playlistManager, min ); } @Override diff --git a/src/alias/id/mpt1327/MPT1327IDNode.java b/src/alias/id/mpt1327/MPT1327IDNode.java index ab591365..a5937cee 100644 --- a/src/alias/id/mpt1327/MPT1327IDNode.java +++ b/src/alias/id/mpt1327/MPT1327IDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class MPT1327IDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public MPT1327IDNode( MPT1327ID id ) + public MPT1327IDNode( PlaylistManager playlistManager, MPT1327ID id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/alias/id/siteID/SiteIDNode.java b/src/alias/id/siteID/SiteIDNode.java index 26569699..e9e2a4b4 100644 --- a/src/alias/id/siteID/SiteIDNode.java +++ b/src/alias/id/siteID/SiteIDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class SiteIDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public SiteIDNode( SiteID id ) + public SiteIDNode( PlaylistManager playlistManager, SiteID id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/alias/id/talkgroup/TalkgroupIDNode.java b/src/alias/id/talkgroup/TalkgroupIDNode.java index 0a8e7e0a..261adc75 100644 --- a/src/alias/id/talkgroup/TalkgroupIDNode.java +++ b/src/alias/id/talkgroup/TalkgroupIDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class TalkgroupIDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public TalkgroupIDNode( TalkgroupID id ) + public TalkgroupIDNode( PlaylistManager playlistManager, TalkgroupID id ) { - super( id ); + super( playlistManager,id ); } @Override diff --git a/src/alias/id/uniqueID/UniqueIDNode.java b/src/alias/id/uniqueID/UniqueIDNode.java index 5c850f15..ebb92f4f 100644 --- a/src/alias/id/uniqueID/UniqueIDNode.java +++ b/src/alias/id/uniqueID/UniqueIDNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class UniqueIDNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public UniqueIDNode( UniqueID id ) + public UniqueIDNode( PlaylistManager playlistManager, UniqueID id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/alias/priority/PriorityNode.java b/src/alias/priority/PriorityNode.java index 178860fe..707ae0f7 100644 --- a/src/alias/priority/PriorityNode.java +++ b/src/alias/priority/PriorityNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasIDNode; import alias.AliasNode; @@ -32,9 +33,9 @@ public class PriorityNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public PriorityNode( Priority priority ) + public PriorityNode( PlaylistManager playlistManager, Priority priority ) { - super( priority ); + super( playlistManager, priority ); } @Override diff --git a/src/alias/record/NonRecordableNode.java b/src/alias/record/NonRecordableNode.java index eb8ba572..082238df 100644 --- a/src/alias/record/NonRecordableNode.java +++ b/src/alias/record/NonRecordableNode.java @@ -25,6 +25,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import alias.AliasID; import alias.AliasIDNode; import alias.AliasNode; @@ -33,9 +34,9 @@ public class NonRecordableNode extends AliasIDNode { private static final long serialVersionUID = 1L; - public NonRecordableNode( NonRecordable id ) + public NonRecordableNode( PlaylistManager playlistManager, NonRecordable id ) { - super( id ); + super( playlistManager, id ); } @Override diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 7e28b70d..1f87f38d 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -8,7 +8,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import audio.metadata.AudioMetadata; import audio.metadata.IMetadataListener; @@ -18,7 +18,6 @@ import audio.squelch.SquelchState; import controller.channel.ChannelEvent; import controller.channel.IChannelEventListener; import dsp.filter.Filters; -import dsp.filter.dc.DCRemovalFilter_RB; import dsp.filter.fir.real.RealFIRFilter_R_R; /** @@ -32,49 +31,31 @@ import dsp.filter.fir.real.RealFIRFilter_R_R; public class AudioModule extends Module implements IAudioPacketProvider, IChannelEventListener, IMetadataListener, - IRealBufferListener, + IFilteredRealBufferListener, ISquelchStateListener, Listener { protected static final Logger mLog = LoggerFactory.getLogger( AudioModule.class ); - /* Determines responsiveness of DC filter to frequency changes */ - private static final float DC_REMOVAL_RATIO = 0.000003f; - /* Provides a unique identifier for this audio module instance to use as a * source identifier for all audio packets */ private static int UNIQUE_ID = 0; private int mSourceID; private AudioMetadata mAudioMetadata; - private ChannelEventListener mChannelEventListener = new ChannelEventListener(); - private SquelchStateListener mSquelchStateListener = new SquelchStateListener(); private SquelchState mSquelchState = SquelchState.SQUELCH; - - private RealFIRFilter_R_R mAudioFilter = new RealFIRFilter_R_R( - Filters.FIR_BANDPASS_AUDIO_48KHZ.getCoefficients(), 2.0f ); - - private DCRemovalFilter_RB mDCRemovalFilter; - private Listener mAudioPacketListener; + +//TODO: remove this -- is being replaced by the DemodulatedAudioFilterModule + private RealFIRFilter_R_R mAudioFilter = new RealFIRFilter_R_R( + Filters.FIR_BANDPASS_AUDIO_48KHZ.getCoefficients(), 2.0f ); - private boolean mRemoveDC = false; - - public AudioModule( boolean record, boolean removeDC ) + public AudioModule( boolean record ) { mSourceID = ++UNIQUE_ID; - mAudioMetadata = new AudioMetadata( mSourceID, record ); - - mRemoveDC = removeDC; - - if( mRemoveDC ) - { - mDCRemovalFilter = new DCRemovalFilter_RB( DC_REMOVAL_RATIO ); - mDCRemovalFilter.setListener( this ); - } } @Override @@ -132,13 +113,8 @@ public class AudioModule extends Module implements IAudioPacketProvider, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { - if( mRemoveDC ) - { - return mDCRemovalFilter; - } - return this; } diff --git a/src/audio/AudioPanel.java b/src/audio/AudioPanel.java index 0d008f01..3e6ba613 100644 --- a/src/audio/AudioPanel.java +++ b/src/audio/AudioPanel.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import settings.SettingsManager; +import source.SourceManager; import source.mixer.MixerChannelConfiguration; import source.mixer.MixerManager; import audio.output.AudioOutput; @@ -43,14 +44,18 @@ public class AudioPanel extends JPanel implements Listener new ImageIcon( "images/audio_unmuted.png" ), 38 ); private SettingsManager mSettingsManager; + private SourceManager mSourceManager; private IAudioController mController; private JButton mMuteButton; private AudioChannelsPanel mAudioChannelsPanel; - public AudioPanel( SettingsManager settingsManager, IAudioController controller ) + public AudioPanel( SettingsManager settingsManager, + SourceManager sourceManager, + IAudioController controller ) { mSettingsManager = settingsManager; + mSourceManager = sourceManager; mController = controller; mController.addControllerListener( this ); @@ -148,7 +153,7 @@ public class AudioPanel extends JPanel implements Listener JMenu outputMenu = new JMenu( "Audio Output" ); MixerChannelConfiguration[] mixerConfigurations = - MixerManager.getInstance().getOutputMixers(); + mSourceManager.getMixerManager().getOutputMixers(); for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) { diff --git a/src/buffer/BufferOverflowException.java b/src/buffer/BufferOverflowException.java deleted file mode 100644 index 4b6e1a57..00000000 --- a/src/buffer/BufferOverflowException.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -public class BufferOverflowException extends Exception -{ - private static final long serialVersionUID = 1L; - - public BufferOverflowException( String text ) - { - super( text ); - } - - public BufferOverflowException( String text, Throwable exc ) - { - super( text, exc ); - } -} diff --git a/src/buffer/BufferOverflowListener.java b/src/buffer/BufferOverflowListener.java deleted file mode 100644 index 2f8acb03..00000000 --- a/src/buffer/BufferOverflowListener.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -public interface BufferOverflowListener -{ - public void overflow( Object source, String message ); -} diff --git a/src/buffer/ByteArrayDelayBuffer.java b/src/buffer/ByteArrayDelayBuffer.java deleted file mode 100644 index fc60703a..00000000 --- a/src/buffer/ByteArrayDelayBuffer.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -public class ByteArrayDelayBuffer -{ - private int mBufferSize; - private byte[][] mBuffer = null; - private int mBufferPointer = 0; - - /** - * Implements a delay buffer for byte arrays. As a new byte[] is placed - * into the buffer, the oldest byte[] is returned - * @param bufferSize - number of byte arrays to buffer - */ - public ByteArrayDelayBuffer( int bufferSize ) - { - mBufferSize = bufferSize; - } - - /** - * Inserts new bytes into the buffer - * - * @return - oldest byte[] from the buffer, or null if the buffer is not - * yet full - */ - public byte[] get( byte[] bytes ) - { - if( mBuffer == null ) - { - mBuffer = new byte[ mBufferSize ][ bytes.length ]; - } - - byte[] returnBytes = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer++ ] = bytes; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - return returnBytes; - } -} diff --git a/src/buffer/ByteArrayListener.java b/src/buffer/ByteArrayListener.java deleted file mode 100644 index 021b8e2a..00000000 --- a/src/buffer/ByteArrayListener.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -public interface ByteArrayListener -{ - public void receive( byte[] data ); -} diff --git a/src/buffer/ByteBufferListener.java b/src/buffer/ByteBufferListener.java deleted file mode 100644 index 7df629a0..00000000 --- a/src/buffer/ByteBufferListener.java +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; - -public interface ByteBufferListener -{ - public void receive( ByteBuffer buffer ); -} diff --git a/src/buffer/ByteBufferProvider.java b/src/buffer/ByteBufferProvider.java deleted file mode 100644 index f85962c3..00000000 --- a/src/buffer/ByteBufferProvider.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - - - -public interface ByteBufferProvider -{ - /** - * Adds a sample listener to receive samples - */ - public void addByteBufferListener( ByteBufferListener listener ); - - /** - * Removes the sample listener, if it is currently registered as a listener - */ - public void removeByteBufferListener( ByteBufferListener listener ); -} diff --git a/src/buffer/ComplexByteBuffer.java b/src/buffer/ComplexByteBuffer.java deleted file mode 100644 index 1abb25dc..00000000 --- a/src/buffer/ComplexByteBuffer.java +++ /dev/null @@ -1,91 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.util.Enumeration; - -import sample.complex.Complex; - -/** - * Provides a complex sample enumeration wrapper around a byte array containing - * interleaved byte-length I & Q sample values in the range of 0 - 255, converting - * those samples into complex samples with value range of -128 to 0 to 127. - */ -public class ComplexByteBuffer implements Enumeration -{ - private static float[] mValues; - - /** - * Creates a lookup table that converts the 8-bit valued range from 0 - 255 - * into scaled float values of -128 to 0 to 127 - */ - static - { - mValues = new float[ 256 ]; - - float scale = (float)( 1.0f / 127.0f ); - - for( int x = 0; x < 256; x++ ) - { - mValues[ x ] = (float)( x - 128 ) * scale; - } - } - - private ByteBuffer mBytes; -// private byte[] mBytes; -// private int mPointer; - - public ComplexByteBuffer( byte[] bytes ) - { - mBytes = ByteBuffer.wrap( bytes ); -// mBytes = bytes; -// mPointer = 0; - } - - public byte[] toArray() - { -// return mBytes; - return mBytes.array(); - } - - /** - * Indicates if there are more complex samples remaining. - */ - @Override - public boolean hasMoreElements() - { -// return mPointer < mBytes.length; - return mBytes.hasRemaining(); - } - - /** - * Returns the next complex sample. - * - * Note: does not check for buffer overrun. Use the hasMoreElements() - * method to ensure you do not read past the end of the buffer. - */ - @Override - public Complex nextElement() - { -// return new ComplexSample( mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ], -// mValues[ ( mBytes[ mPointer++ ] & 0xFF ) ] ); - return new Complex( mValues[ ( mBytes.get() & 0xFF ) ], - mValues[ ( mBytes.get() & 0xFF ) ] ); - } -} diff --git a/src/buffer/ComplexSampleBufferAssembler.java b/src/buffer/ComplexSampleBufferAssembler.java deleted file mode 100644 index 935696f1..00000000 --- a/src/buffer/ComplexSampleBufferAssembler.java +++ /dev/null @@ -1,73 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Arrays; - -import sample.complex.Complex; - -public class ComplexSampleBufferAssembler -{ - private ByteBuffer mByteBuffer; - private int mSizeInBytes; - - public ComplexSampleBufferAssembler( int size ) - { - mSizeInBytes = size * 4; - - //Allocate a byte buffer with 4 bytes per sample (ie 2 x 16-bit samples) - mByteBuffer = ByteBuffer.allocate( mSizeInBytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } - - /** - * Load the sample's bytes, left then right, little endian, into the - * backing byte array - */ - public void put( Complex sample ) - { - mByteBuffer.putShort( (short)( sample.left() * Short.MAX_VALUE ) ); - mByteBuffer.putShort( (short)( sample.right() * Short.MAX_VALUE ) ); - } - - /** - * Indicates if there is room for more samples in the buffer, or if it is full - */ - public boolean hasRemaining() - { - return mByteBuffer.hasRemaining(); - } - - /** - * Returns a copy of the byte array backing this buffer - */ - public ByteBuffer get() - { - return ByteBuffer.wrap( Arrays.copyOf( mByteBuffer.array(), mSizeInBytes ) ); - } - - /** - * Clear the bytes from the underlying buffer and reset the pointer to 0. - */ - public void clear() - { - mByteBuffer.clear(); - } -} diff --git a/src/buffer/DelayBuffer.java b/src/buffer/DelayBuffer.java deleted file mode 100644 index 001058db..00000000 --- a/src/buffer/DelayBuffer.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import sample.Listener; - -/** - * Delay Buffer - implements a circular delay buffer backed by an object array - */ -public class DelayBuffer implements Listener -{ - private Object[] mBuffer; - private int mBufferPointer = 0; - private Listener mListener; - - public DelayBuffer( int length ) - { - mBuffer = new Object[ length ]; - } - - @Override - @SuppressWarnings( "unchecked" ) - public void receive( T value ) - { - Object delayed = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer ] = value; - - mBufferPointer++; - - /* Wrap the buffer pointer around to 0 when necessary */ - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - - if( mListener != null && delayed != null ) - { - mListener.receive( (T)delayed ); - } - } - - public void setListener( Listener listener ) - { - mListener = listener; - } - - public void removeListener( Listener listener ) - { - mListener = null; - } -} diff --git a/src/buffer/FloatArrayCircularAveragingBuffer.java b/src/buffer/FloatArrayCircularAveragingBuffer.java deleted file mode 100644 index 5003d789..00000000 --- a/src/buffer/FloatArrayCircularAveragingBuffer.java +++ /dev/null @@ -1,97 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.util.ArrayList; -import java.util.Arrays; - -public class FloatArrayCircularAveragingBuffer -{ - private ArrayList mBuffer; - private int mBufferPointer; - private int mBufferSize; - private float[] mAverage; - - public FloatArrayCircularAveragingBuffer( int bufferSize ) - { - mBufferSize = bufferSize; - mBuffer = new ArrayList(); - } - - /** - * Replaces the oldest float array element in the buffer with this new - * float array element, and then returns the average of the buffer contents - * with the newElement values included in the average array - */ - public float[] get( float[] newElement ) - { - /** - * If we've never setup the average array, or the newElements length is - * different than what we've been processing, reset the buffer & averages - */ - if( mAverage == null || mAverage.length != newElement.length ) - { - reset( newElement.length ); - } - - float[] oldElement = mBuffer.get( mBufferPointer ); - - for( int x = 0; x < newElement.length; x++ ) - { - /** - * Change the current average value in each array element by the - * difference of the new element minus the old element, divided - * by the buffer size ... average it - */ - mAverage[ x ] += - ( ( newElement[ x ] - oldElement[ x ] ) / mBufferSize ); - } - - mBuffer.set( mBufferPointer, newElement ); - - mBufferPointer++; - - if( mBufferPointer >= mBufferSize ) - { - mBufferPointer = 0; - } - - return mAverage; - } - - /** - * Clears buffer and average and fills buffer with zero-valued float arrays - */ - private void reset( int arrayLength ) - { - mAverage = new float[ arrayLength ]; - Arrays.fill( mAverage, 0.0f ); - - mBuffer.clear(); - mBufferPointer = 0; - - float[] emptyArray = new float[ arrayLength ]; - Arrays.fill( emptyArray, 0.0f ); - - for( int x = 0; x < mBufferSize; x++ ) - { - mBuffer.add( emptyArray ); - } - } - -} diff --git a/src/buffer/FloatBufferAssembler.java b/src/buffer/FloatBufferAssembler.java deleted file mode 100644 index 651a1794..00000000 --- a/src/buffer/FloatBufferAssembler.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.util.Arrays; - -import sample.Listener; - - -/** - * Assembles single samples into sample blocks (ie arrays) for broadcast to - * registered SampleBlockListeners - */ -public class FloatBufferAssembler implements Listener -{ - private Float[] mFloatBuffer; - private int mBufferSize; - private int mBufferPointer = 0; - private Listener mListener; - - public FloatBufferAssembler( int bufferSize ) - { - mBufferSize = bufferSize; - mFloatBuffer = new Float[ mBufferSize ]; - } - - /** - * Stores received samples in the sample block and manages the storage - * pointer. Sends sample blocks when full. - */ - @Override - public void receive( Float sample ) - { - mFloatBuffer[ mBufferPointer++ ] = sample; - - if( mBufferPointer == mBufferSize ) - { - if( mListener != null ) - { - mListener.receive( Arrays.copyOf( mFloatBuffer, mBufferSize ) ); - } - - mBufferPointer = 0; - } - } - - /** - * Adds a new sampleblock listener to receive sample blocks as they are - * assembled from the sample stream - */ - public void setListener( Listener listener ) - { - mListener = listener; - } -} diff --git a/src/buffer/FloatCircularAveragingBuffer.java b/src/buffer/FloatCircularAveragingBuffer.java deleted file mode 100644 index 6a05ea61..00000000 --- a/src/buffer/FloatCircularAveragingBuffer.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.util.Arrays; - -/** - * Circular sample buffer - allocates a buffer and stores samples in a circular - * fashion, overwriting older samples with newly arrived samples. Initially - * fills buffer with 0-valued samples - * - * Produces no output. Obtain the average buffer value on-demand via the - * getAverage() method - */ -public class FloatCircularAveragingBuffer -{ - private float[] mBuffer; - private int mBufferPointer = 0; - private int mSampleCountForAveraging; - private int mCurrentSampleCount = 0; - private float mAverage = 0.0f; - - public FloatCircularAveragingBuffer( int size, int sampleCountForAveraging ) - { - mBuffer = new float[ size ]; - mSampleCountForAveraging = sampleCountForAveraging; - Arrays.fill( mBuffer, 0.0f ); - } - - /** - * Convenience wrapper to throw away to put the new sample and throwaway - * the oldest sample - */ - public void put( float newestSample ) - { - get( newestSample ); - } - - /** - * Loads the newest sample into the buffer and returns the oldest sample - */ - public float get( float newestSample ) - { - float oldestSample = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer ] = newestSample; - - mBufferPointer++; - - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - - mCurrentSampleCount++; - - if( mCurrentSampleCount >= mSampleCountForAveraging ) - { - calculateAverage(); - - mCurrentSampleCount = 0; - } - - return oldestSample; - } - - /** - * Calculates the average of the maximum and minimum values present in - * the buffer - */ - private void calculateAverage() - { - float min = mBuffer[ 0 ]; - float max = mBuffer[ 0 ]; - - for( int x = 0; x < mBuffer.length; x++ ) - { - if( mBuffer[ x ] < min ) - { - min = mBuffer[ x ]; - } - if( mBuffer[ x ] > max ) - { - max = mBuffer[ x ]; - } - } - - mAverage = (float)( ( min + max ) / 2.0f ); - } - - public float getAverage() - { - return mAverage; - } -} diff --git a/src/buffer/FloatCircularAveragingBuffer2.java b/src/buffer/FloatCircularAveragingBuffer2.java deleted file mode 100644 index da27d207..00000000 --- a/src/buffer/FloatCircularAveragingBuffer2.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.util.Arrays; - -/** - * Circular sample buffer - stores samples in a circular buffer - * overwriting older samples with newly arrived samples. Initially - * fills buffer with 0-valued samples - * - * Continuously calculates the average of the samples in the buffer, with the - * average available from the average() method. - */ -public class FloatCircularAveragingBuffer2 -{ - private float[] mBuffer; - private int mBufferPointer = 0; - private float mAverage = 0.0f; - - public FloatCircularAveragingBuffer2( int size ) - { - mBuffer = new float[ size ]; - Arrays.fill( mBuffer, 0.0f ); - } - - /** - * Loads the newest sample into the buffer and returns the oldest sample - */ - public float get( float newestSample ) - { - float oldestSample = mBuffer[ mBufferPointer ]; - - mBuffer[ mBufferPointer ] = newestSample; - - mBufferPointer++; - - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - - mAverage = mAverage - ( oldestSample / mBuffer.length ) + - ( newestSample / mBuffer.length ); - - return oldestSample; - } - - public float average() - { - return mAverage; - } -} diff --git a/src/buffer/FloatSampleBuffer.java b/src/buffer/FloatSampleBuffer.java deleted file mode 100644 index bca43189..00000000 --- a/src/buffer/FloatSampleBuffer.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; - - -public class FloatSampleBuffer -{ - private static final long serialVersionUID = 1L; - - private int mSize; - private float[] mBuffer; - private int mBufferPointer = 0; - - public FloatSampleBuffer( int size ) - { - super(); - mBuffer = new float[ size ]; - } - - public void put( float sample ) - { - if( !isFull() ) - { - mBuffer[ mBufferPointer++ ] = sample; - } - } - - public void reset() - { - mBufferPointer = 0; - } - - public boolean isFull() - { - return mBufferPointer >= mBuffer.length; - } - - public byte[] getBytes() - { - ByteBuffer bb = ByteBuffer.allocate( mBuffer.length * 2 ); - - for( int x = 0; x < mBuffer.length; x++ ) - { - bb.putShort( ((Float)mBuffer[ x ]).shortValue() ); - } - - return bb.array(); - } -} diff --git a/src/buffer/FloatSampleBufferAssembler.java b/src/buffer/FloatSampleBufferAssembler.java deleted file mode 100644 index de4d53c2..00000000 --- a/src/buffer/FloatSampleBufferAssembler.java +++ /dev/null @@ -1,72 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -public class FloatSampleBufferAssembler -{ - private ByteBuffer mByteBuffer; - private int mSizeInBytes; - - public FloatSampleBufferAssembler( int size ) - { - mSizeInBytes = size * 2; - - //Allocate a byte buffer with 2 bytes per sample (ie 1 x 16-bit sample) - mByteBuffer = ByteBuffer.allocate( mSizeInBytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } - - /** - * Load the sample's bytes, left then right, little endian, into the - * backing byte array - */ - public void put( float sample ) - { - mByteBuffer.putShort( (short)( sample * 32768 ) ); - } - - /** - * Indicates if there is room for more samples in the buffer, or if it is full - */ - public boolean hasRemaining() - { - return mByteBuffer.hasRemaining(); - } - - /** - * Returns a copy of the byte array backing this buffer - */ - public ByteBuffer get() - { - return mByteBuffer; -// return ByteBuffer.wrap( Arrays.copyOf( mByteBuffer.array(), mSizeInBytes ) ); - } - - /** - * Clear the bytes from the underlying buffer and reset the pointer to 0. - */ - public void reset() - { -// mByteBuffer.clear(); - mByteBuffer = ByteBuffer.allocate( mSizeInBytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } -} diff --git a/src/buffer/FloatTappedCircularBuffer.java b/src/buffer/FloatTappedCircularBuffer.java deleted file mode 100644 index 5d0c3e7f..00000000 --- a/src/buffer/FloatTappedCircularBuffer.java +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - - -/** - * Implements a FIFO circular buffer for floats that provides access - * to each sample by index number, where index 0 is the newest/latest - * complex sample and index (length - 1) is the oldest sample in the buffer. - */ -public class FloatTappedCircularBuffer -{ - private Float[] mBuffer; - private int mBufferPointer = 0; - - public FloatTappedCircularBuffer( int length ) - { - mBuffer = new Float[ length ]; - - //Pre-load the buffer with 0-valued samples - for( int x = 0; x < length; x++ ) - { - mBuffer[ x ] = Float.valueOf( 0.0f ); - } - } - - /** - * Adds the sample to the buffer and removes the oldest sample - * @param sample - */ - public void add( Float sample ) - { - mBuffer[ mBufferPointer++ ] = sample; - - //Reset the buffer pointer once it points past the end of the buffer - if( mBufferPointer >= mBuffer.length ) - { - mBufferPointer = 0; - } - } - - /** - * Retrieves a sample currently in the buffer by index position. - * - * Index 0 is the newest/latest sample in the buffer and index (length - 1) - * is the oldest. - * - * Note: the buffer is preloaded with complex samples of 0,0 value. - * - * @param index where 0 is the newest/latest sample and length-1 is the oldest - * @return complex sample from the buffer. - * @throws ArrayIndexOutOfBoundsException if you attempt to access a sample - * outside of the buffer size - */ - public Float get( int index ) - { - /* Since the samples are stored in a forward fashion, we have to access - * the samples in reverse to comply with the access contract, where - * index 0 is the newest and index (length -1) is the oldest, all - * relative to the current buffer pointer */ - - int pointer = mBuffer.length - 1 + mBufferPointer - index; - - if( pointer >= mBuffer.length ) - { - pointer -= mBuffer.length; - } - - return mBuffer[ pointer ]; - } -} diff --git a/src/buffer/FloatToByteBufferAssembler.java b/src/buffer/FloatToByteBufferAssembler.java deleted file mode 100644 index 5f24ffce..00000000 --- a/src/buffer/FloatToByteBufferAssembler.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import sample.Broadcaster; -import sample.Listener; - -public class FloatToByteBufferAssembler implements Listener -{ - private Broadcaster mBroadcaster = new Broadcaster(); - - private int mBufferSize = 0; - private ByteBuffer mBuffer; - - public FloatToByteBufferAssembler( int bufferSize ) - { - mBufferSize = bufferSize; - resetBuffer(); - } - - public void addByteBufferListener( Listener listener ) - { - mBroadcaster.addListener( listener ); - } - - public void removeByteBufferListener( Listener listener ) - { - mBroadcaster.removeListener( listener ); - } - - private void resetBuffer() - { - mBuffer = ByteBuffer.allocate( mBufferSize ); - mBuffer.order( ByteOrder.BIG_ENDIAN ); - } - - @Override - public void receive( Float sample ) - { - mBuffer.putShort( Float.valueOf( sample ).shortValue() ); - - if( mBuffer.position() >= mBufferSize ) - { - //Buffer is full ... send it - mBroadcaster.receive( mBuffer ); - - //Reset the buffer - resetBuffer(); - } - } -} diff --git a/src/buffer/StereoByteBuffer.java b/src/buffer/StereoByteBuffer.java deleted file mode 100644 index 6e4e6db4..00000000 --- a/src/buffer/StereoByteBuffer.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Enumeration; - -import sample.complex.Complex; -import source.mixer.MixerChannel; - -public class StereoByteBuffer implements Enumeration -{ - private ByteBuffer mByteBuffer; - - public StereoByteBuffer( byte[] bytes, int frames ) - { - mByteBuffer = ByteBuffer.wrap( bytes ); - mByteBuffer.order( ByteOrder.LITTLE_ENDIAN ); - } - - public byte[] array() - { - return mByteBuffer.array(); - } - - public ByteBuffer getChannel( MixerChannel channel ) - { - return StereoToMonoByteBufferConverter.getChannel( mByteBuffer, channel ); - } - - @Override - public boolean hasMoreElements() - { - return mByteBuffer.hasRemaining(); - } - - @Override - public Complex nextElement() - { - return new Complex( (float)mByteBuffer.getShort(), - (float)mByteBuffer.getShort() ); - } -} diff --git a/src/buffer/StereoByteBufferListener.java b/src/buffer/StereoByteBufferListener.java deleted file mode 100644 index 641305c1..00000000 --- a/src/buffer/StereoByteBufferListener.java +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - - -public interface StereoByteBufferListener -{ - public void receive( StereoByteBuffer buffer ); -} diff --git a/src/buffer/StereoByteBufferProvider.java b/src/buffer/StereoByteBufferProvider.java deleted file mode 100644 index 76f91930..00000000 --- a/src/buffer/StereoByteBufferProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - - -public interface StereoByteBufferProvider -{ - /** - * Adds a sample listener to receive samples - */ - public void addStereoByteBufferListener( StereoByteBufferListener listener ); - - /** - * Removes the sample listener, if it is currently registered as a listener - */ - public void removeStereoByteBufferListener( StereoByteBufferListener listener ); -} diff --git a/src/buffer/StereoToMonoByteBufferConverter.java b/src/buffer/StereoToMonoByteBufferConverter.java deleted file mode 100644 index 55e43aca..00000000 --- a/src/buffer/StereoToMonoByteBufferConverter.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package buffer; - -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -import source.mixer.MixerChannel; - -public class StereoToMonoByteBufferConverter -{ - public static ByteBuffer getChannel( ByteBuffer buffer, MixerChannel channel ) - { - byte[] channelBuffer = new byte[ buffer.capacity() / 2 ]; - - - int channelBufferPointer = 0; - - if( channel == MixerChannel.RIGHT ) - { - //throw away the first two bytes - buffer.get(); - buffer.get(); - } - - while( channelBufferPointer < channelBuffer.length ) - { - if( buffer.hasRemaining() ) - { - channelBuffer[ channelBufferPointer++ ] = buffer.get(); - channelBuffer[ channelBufferPointer++ ] = buffer.get(); - } - - if( buffer.hasRemaining() ) - { - buffer.get(); - buffer.get(); - } - } - - return ByteBuffer.wrap( channelBuffer ).order( ByteOrder.LITTLE_ENDIAN ); - } -} diff --git a/src/controller/ConfigurableNode.java b/src/controller/ConfigurableNode.java index f9a6e3be..77da7453 100644 --- a/src/controller/ConfigurableNode.java +++ b/src/controller/ConfigurableNode.java @@ -17,14 +17,24 @@ ******************************************************************************/ package controller; +import playlist.PlaylistManager; public abstract class ConfigurableNode extends BaseNode { private static final long serialVersionUID = 1L; + + private PlaylistManager mPlaylistManager; - public ConfigurableNode( Object object ) + public ConfigurableNode( PlaylistManager playlistManager, Object object ) { super( object ); + + mPlaylistManager = playlistManager; + } + + protected PlaylistManager getPlaylistManager() + { + return mPlaylistManager; } /** @@ -32,7 +42,7 @@ public abstract class ConfigurableNode extends BaseNode */ public void save() { - getModel().getResourceManager().getPlaylistManager().save(); + mPlaylistManager.save(); BaseNode parent = (BaseNode)getParent(); diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java index 8bbd48c3..692ee7ee 100644 --- a/src/controller/ConfigurationControllerModel.java +++ b/src/controller/ConfigurationControllerModel.java @@ -28,9 +28,14 @@ import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; -import map.IconManager; import module.decode.DecoderType; + +import playlist.PlaylistManager; import playlist.PlaylistNode; + +import settings.SettingsManager; +import source.SourceManager; +import source.recording.RecordingGroupNode; import source.tuner.Tuner; import source.tuner.TunerGroupNode; import source.tuner.TunerNode; @@ -39,14 +44,14 @@ import controller.channel.Channel; import controller.channel.ChannelModel; import controller.channel.ChannelNode; +import controller.channel.ChannelProcessingManager; + public class ConfigurationControllerModel extends DefaultTreeModel { private static final long serialVersionUID = 1L; private JTree mTree; - private ResourceManager mResourceManager; - private ArrayList mTunerSelectionListeners = new ArrayList(); @@ -54,7 +59,11 @@ public class ConfigurationControllerModel extends DefaultTreeModel private TunerGroupNode mTunerGroupNode; // private RecordingGroupNode mRecordingGroupNode; - private IconManager mIconManagerFrame; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; + private SourceManager mSourceManager; private ChannelModel mChannelModel; /** @@ -63,14 +72,21 @@ public class ConfigurationControllerModel extends DefaultTreeModel * design perspective, the model and the control are necessarily merged to * work well with the JTree as the system view. */ - public ConfigurationControllerModel( ChannelModel channelModel, - ResourceManager resourceManager ) + public ConfigurationControllerModel( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SettingsManager settingsManager, + SourceManager sourceManager ) { super( new BaseNode( null ) ); mChannelModel = channelModel; - - mResourceManager = resourceManager; + + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; + mSourceManager = sourceManager; /** * Give the root node a reference to this model, so that all nodes can @@ -84,23 +100,6 @@ public class ConfigurationControllerModel extends DefaultTreeModel return mTree; } - public void showIconManager() - { - if( mIconManagerFrame == null ) - { - mIconManagerFrame = new IconManager( mResourceManager, mTree ); - - mResourceManager.getSettingsManager().addListener( mIconManagerFrame ); - } - - if( !mIconManagerFrame.isVisible() ) - { - mIconManagerFrame.setVisible( true ); - } - - mIconManagerFrame.toFront(); - } - /** * init() gets invoked manually/externally, after all of the references * are established in the constructor @@ -124,7 +123,8 @@ public class ConfigurationControllerModel extends DefaultTreeModel /** * Add the playlist node */ - mPlaylistNode = new PlaylistNode(); + mPlaylistNode = new PlaylistNode( mChannelModel, mChannelProcessingManager, + mPlaylistManager, mSettingsManager, mSourceManager ); insertNodeInto( mPlaylistNode, (MutableTreeNode)root, 1 ); mPlaylistNode.loadPlaylist(); } @@ -137,11 +137,6 @@ public class ConfigurationControllerModel extends DefaultTreeModel } } - public ResourceManager getResourceManager() - { - return mResourceManager; - } - public void setTree( JTree tree ) { mTree = tree; @@ -149,11 +144,13 @@ public class ConfigurationControllerModel extends DefaultTreeModel private void addTuners( TunerGroupNode parent ) { - List tuners = mResourceManager.getTunerManager().getTuners(); + List tuners = mSourceManager.getTunerManager().getTuners(); for( Tuner tuner: tuners ) { - TunerNode child = new TunerNode( mChannelModel, tuner ); + TunerNode child = new TunerNode( mChannelModel, + mChannelProcessingManager, mPlaylistManager, mSettingsManager, + tuner ); insertNodeInto( child, parent, parent.getChildCount() ); diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index 64b40a6d..e8612478 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -33,8 +33,10 @@ import module.decode.event.CallEventPanel; import module.decode.event.MessageActivityPanel; import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; +import playlist.PlaylistEditor; import playlist.PlaylistManager; import settings.SettingsManager; +import source.SourceManager; import spectrum.ChannelSpectrumPanel; import audio.AudioManager; import audio.AudioPanel; @@ -53,15 +55,16 @@ public class ControllerPanel extends JPanel private CallEventPanel mCallEventPanel; - private MessageActivityPanel mMessageActivityPanel = - new MessageActivityPanel(); + private MessageActivityPanel mMessageActivityPanel; private ChannelSpectrumPanel mChannelSpectrumPanel; - private JideTabbedPane mTabbedPane; + private PlaylistEditor mPlaylistEditor; + + private JideTabbedPane mTabbedPane; protected ConfigurationTreePanel mSystemControlViewPanel; - protected ConfigurationEditor mConfigurationEditor; + protected OldConfigurationEditor mConfigurationEditor; protected JideSplitPane mSystemControlSplitPane; protected JTable mChannelActivityTable = new JTable(); @@ -78,20 +81,30 @@ public class ControllerPanel extends JPanel ChannelProcessingManager channelProcessingManager, MapService mapService, PlaylistManager playlistManager, - SettingsManager settingsManager ) + SettingsManager settingsManager, + SourceManager sourceManager ) { mChannelModel = channelModel; mController = controller; mSettingsManager = settingsManager; - mAudioPanel = new AudioPanel( mSettingsManager, audioManager ); + mAudioPanel = new AudioPanel( mSettingsManager, sourceManager, audioManager ); - mMapPanel = new MapPanel( mapService, mSettingsManager, channelProcessingManager ); + mMapPanel = new MapPanel( mapService, mSettingsManager ); - mCallEventPanel = new CallEventPanel( mSettingsManager ); - - mChannelStateList = new ChannelList( playlistManager, mSettingsManager ); + mMessageActivityPanel = new MessageActivityPanel( channelProcessingManager ); + + mCallEventPanel = new CallEventPanel( mSettingsManager, + channelProcessingManager ); + mChannelSpectrumPanel = new ChannelSpectrumPanel( mSettingsManager, + channelProcessingManager ); + + mChannelStateList = new ChannelList( channelModel, channelProcessingManager, + playlistManager, mSettingsManager ); + + mPlaylistEditor = new PlaylistEditor( channelModel, playlistManager, sourceManager ); + init(); } @@ -102,7 +115,7 @@ public class ControllerPanel extends JPanel "[grow,fill]") ); //System Configuration View and Editor - mConfigurationEditor = new ConfigurationEditor(); + mConfigurationEditor = new OldConfigurationEditor(); mSystemControlViewPanel = new ConfigurationTreePanel( mController ); mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor ); @@ -112,8 +125,6 @@ public class ControllerPanel extends JPanel mSystemControlSplitPane.add( mSystemControlViewPanel ); mSystemControlSplitPane.add( mConfigurationEditor ); - mChannelSpectrumPanel = new ChannelSpectrumPanel( mSettingsManager ); - //Tabbed View - configuration, calls, messages, map mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont( this.getFont() ); @@ -122,10 +133,8 @@ public class ControllerPanel extends JPanel mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); mTabbedPane.addTab( "Messages", mMessageActivityPanel ); - - JTable channelsTable = new JTable( mChannelModel ); - JScrollPane channelsScroller = new JScrollPane( channelsTable ); - mTabbedPane.addTab( "Channels", channelsScroller ); + + mTabbedPane.addTab( "Playlist", mPlaylistEditor ); /** * Change listener to enable/disable the channel spectrum display diff --git a/src/controller/ConfigurationEditor.java b/src/controller/OldConfigurationEditor.java similarity index 95% rename from src/controller/ConfigurationEditor.java rename to src/controller/OldConfigurationEditor.java index 84a99a30..29d504ee 100644 --- a/src/controller/ConfigurationEditor.java +++ b/src/controller/OldConfigurationEditor.java @@ -26,7 +26,7 @@ import javax.swing.JTree; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; -public class ConfigurationEditor extends JPanel +public class OldConfigurationEditor extends JPanel implements TreeSelectionListener { private static final long serialVersionUID = 1L; @@ -34,7 +34,7 @@ public class ConfigurationEditor extends JPanel /** * Host for configuration editor display panels */ - public ConfigurationEditor() + public OldConfigurationEditor() { init(); } diff --git a/src/controller/ResourceManager.java b/src/controller/ResourceManager.java deleted file mode 100644 index a71de851..00000000 --- a/src/controller/ResourceManager.java +++ /dev/null @@ -1,177 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - -import map.MapService; -import module.log.EventLogManager; -import playlist.PlaylistManager; -import record.RecorderManager; -import settings.SettingsManager; -import source.SourceManager; -import source.recording.RecordingSourceManager; -import source.tuner.TunerManager; -import audio.AudioManager; -import controller.channel.ChannelModel; - -/** - * Manager for system wide resources - */ -public class ResourceManager -{ - private AudioManager mAudioManager; - private EventLogManager mEventLogManager; - private PlaylistManager mPlaylistManager; - private RecorderManager mRecorderManager; - private RecordingSourceManager mRecordingSourceManager; - private SettingsManager mSettingsManager; - private SourceManager mSourceManager; - private TunerManager mTunerManager; - private ConfigurationControllerModel mController; - private MapService mMapService; - private ThreadPoolManager mThreadPoolManager; - - public ResourceManager( PlaylistManager playlistManager, - ThreadPoolManager threadPoolManager, - ChannelModel channelModel ) - { - this( threadPoolManager, - new SettingsManager(), - playlistManager, - new EventLogManager(), - channelModel ); - } - - public ResourceManager( ThreadPoolManager threadPoolManager, - SettingsManager settingsManager, - PlaylistManager playlistManager, - EventLogManager eventLogManager, - ChannelModel channelModel ) - { - mThreadPoolManager = threadPoolManager; - mSettingsManager = settingsManager; - mPlaylistManager = playlistManager; - mEventLogManager = eventLogManager; - - /** - * ChannelManager requires a reference to the ResourceManager so that - * channel processing chains can access system wide resources - */ - mAudioManager = new AudioManager( mThreadPoolManager ); - mRecorderManager = new RecorderManager( mThreadPoolManager ); - mSourceManager = new SourceManager( this ); - mRecordingSourceManager = new RecordingSourceManager( this ); - mTunerManager = new TunerManager( this ); - mController = new ConfigurationControllerModel( channelModel, this ); - mMapService = new MapService( this ); - } - - public AudioManager getAudioManager() - { - return mAudioManager; - } - - public void setAudioManager( AudioManager audioManager ) - { - mAudioManager = audioManager; - } - - public ConfigurationControllerModel getController() - { - return mController; - } - - public ThreadPoolManager getThreadPoolManager() - { - return mThreadPoolManager; - } - - public void setController( ConfigurationControllerModel controller ) - { - mController = controller; - } - - public TunerManager getTunerManager() - { - return mTunerManager; - } - - public MapService getMapService() - { - return mMapService; - } - - public SettingsManager getSettingsManager() - { - return mSettingsManager; - } - - public void setSettingsManager( SettingsManager manager ) - { - mSettingsManager = manager; - } - - public PlaylistManager getPlaylistManager() - { - return mPlaylistManager; - } - - public void setPlaylistManager( PlaylistManager aliasManager ) - { - mPlaylistManager = aliasManager; - } - - public EventLogManager getEventLogManager() - { - return mEventLogManager; - } - - public void setEventLogManager( EventLogManager eventLogManager ) - { - mEventLogManager = eventLogManager; - } - - public RecorderManager getRecorderManager() - { - return mRecorderManager; - } - - public void setRecorderManager( RecorderManager recorderManager ) - { - mRecorderManager = recorderManager; - } - - public SourceManager getSourceManager() - { - return mSourceManager; - } - - public void setSourceManager( SourceManager sourceManager ) - { - mSourceManager = sourceManager; - } - - public RecordingSourceManager getRecordingSourceManager() - { - return mRecordingSourceManager; - } - - public void setRecordingSourceManager( RecordingSourceManager manager ) - { - mRecordingSourceManager = manager; - } -} diff --git a/src/controller/channel/AbstractChannelEditor.java b/src/controller/channel/AbstractChannelEditor.java index 41c3b4aa..2ef02f60 100644 --- a/src/controller/channel/AbstractChannelEditor.java +++ b/src/controller/channel/AbstractChannelEditor.java @@ -17,23 +17,22 @@ ******************************************************************************/ package controller.channel; -import controller.Editor; -public abstract class AbstractChannelEditor extends Editor +public abstract class AbstractChannelEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; - protected ChannelNode mChannelNode; + protected Channel mChannel; - public AbstractChannelEditor( ChannelNode channel ) + public AbstractChannelEditor( Channel channel ) { super(); - mChannelNode = channel; + mChannel = channel; } - public ChannelNode getChannelNode() + public Channel getChannel() { - return mChannelNode; + return mChannel; } public abstract void save(); diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index 3da95791..b7d290a8 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,35 +17,21 @@ ******************************************************************************/ package controller.channel; -import java.util.List; - import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; -import message.Message; -import module.Module; -import module.ProcessingChain; -import module.decode.DecoderFactory; import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEventModel; -import module.decode.event.MessageActivityModel; -import module.decode.state.ChannelState; -import module.log.MessageEventLogger; import module.log.config.EventLogConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import record.RecorderManager; -import record.RecorderType; import record.config.RecordConfiguration; -import source.Source; -import source.SourceException; import source.SourceType; import source.config.SourceConfigFactory; import source.config.SourceConfigRecording; @@ -53,27 +39,25 @@ import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; -import alias.AliasList; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; -import controller.ResourceManager; + +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.config.Configuration; -import filter.FilterSet; @XmlSeeAlso( { Configuration.class } ) @XmlRootElement( name = "channel" ) -public class Channel extends Configuration +public class Channel extends Configuration implements IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( Channel.class ); - private static final boolean DISABLED = false; - - public enum ChannelType - { - STANDARD, - TRAFFIC - }; + // Standard channels are persisted and traffic channels are temporary + public enum ChannelType { STANDARD, TRAFFIC }; + + // Static unique channel identifier tracking + private static int UNIQUE_ID = 0; + private DecodeConfiguration mDecodeConfiguration = DecodeConfigFactory.getDefaultDecodeConfiguration(); private AuxDecodeConfiguration mAuxDecodeConfiguration = @@ -84,134 +68,67 @@ public class Channel extends Configuration new EventLogConfiguration(); private RecordConfiguration mRecordConfiguration = new RecordConfiguration(); - private ResourceManager mResourceManager; private String mAliasListName; - private String mSystem; - private String mSite; - private String mName; + private String mSystem = "System"; + private String mSite = "Site"; + private String mName = "Channel"; + private boolean mEnabled; private boolean mSelected; - private ChannelType mChannelType; - private ChannelState mChannelState; - private CallEventModel mCallEventModel; - private MessageActivityModel mMessageActivityModel; - private MessageEventLogger mEventLogger; - private ProcessingChain mProcessingChain; + private TunerChannel mTunerChannel = null; - private long mTrafficChannelTimeout = - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; + private ChannelType mChannelType = ChannelType.STANDARD; + + private int mChannelID; + private int mChannelFrequencyCorrection = 0; /** - * Constructs a new standard channel with a default name of "New Channel" - */ - public Channel() - { - this( "New Channel", ChannelType.STANDARD ); - } - - /** - * Constructs a new standard channel with the specified channel name - */ - public Channel( String channelName ) - { - this( channelName, ChannelType.STANDARD ); - } - - /** - * Constructs a new channel with the specified channel name and type. + * Channel represents a complete set of configurations needed to setup and + * manage a processing chain and/or manage as a set of business objects that + * can be displayed in graphical components. * * @param channelName * @param channelType */ public Channel( String channelName, ChannelType channelType ) { - mName = channelName; + this( channelName ); mChannelType = channelType; } - + /** - * Indicates if the channel has Automatic or Direct Frequency Control + * Constructs a new standard channel with the specified channel name */ - public boolean hasFrequencyControl() + public Channel( String channelName ) { - return mProcessingChain != null && - mProcessingChain.hasFrequencyCorrectionControl(); - } - - public long getFrequencyCorrection() - { - if( hasFrequencyControl() ) - { - return mProcessingChain.getFrequencyCorrectionControl().getErrorCorrection(); - } - - return 0; + this(); + mName = channelName; } /** - * Sets the channel state traffic channel call end timer to the specified - * value. - */ - public void setTrafficChannelTimeout( long milliseconds ) - { - mTrafficChannelTimeout = milliseconds; - - if( mChannelState != null ) - { - mChannelState.setTrafficChannelTimeout( milliseconds ); - } - } - - /** - * Sets the resource manager responsible for this channel and registers - * the channel manager as a listener to this channel. Fires channel add - * channel event to the newly added channel manager channel listener. - * - * Note: resource manager instance is applied after object construction - * since we construct the channel objects from the xml playlist + * Constructs a new standard channel with a default name of "Channel" */ //TODO: remove this - public void setResourceManager( ResourceManager resourceManager ) + public Channel() { - mResourceManager = resourceManager; - - /* If we're enabled, fire enable changed event to get processing started */ - if( mEnabled ) - { - enableChanged(); - } + mChannelID = UNIQUE_ID++; } //TODO: remove this - @XmlTransient - public ResourceManager getResourceManager() - { - return mResourceManager; - } - /** - * Returns the processing chain for this channel. If the processing chain - * is not currently constructed and running, this method will return null. + * Unique identifier for this channel. Value is transient (not persisted). */ @XmlTransient - public ProcessingChain getProcessingChain() + public int getChannelID() { - return mProcessingChain; + return mChannelID; } /** - * Indicates if this channel has a processing chain constructed and it - * is processing. - */ - public boolean isProcessing() - { - return mProcessingChain != null && mProcessingChain.processing(); - } - - /** - * Indicates if this channel has been selected for audio output + * Indicates if this channel has been selected for prioritized audio output + * and display of processing chain products */ @XmlTransient public boolean isSelected() @@ -224,100 +141,12 @@ public class Channel extends Configuration mSelected = selected; } - @XmlTransient - public CallEventModel getCallEventModel() - { - return mCallEventModel; - } - - /** - * Returns the MessageEventLogger for this processing chain - */ - @XmlTransient - public MessageEventLogger getEventLogger() - { - return mEventLogger; - } - - /** - * Sets the MessageEventLogger for this processing chain - */ - public void setEventLogger( MessageEventLogger eventLogger ) - { - mEventLogger = eventLogger; - } - - @XmlTransient - public MessageActivityModel getMessageActivityModel() - { - return mMessageActivityModel; - } - - @XmlTransient - public ChannelState getChannelState() - { - return mChannelState; - } - @XmlTransient public ChannelType getChannelType() { return mChannelType; } - - /** - * Orderly shutdown method when this channel is going to be deleted. - */ - public void dispose() - { - setEnabled( DISABLED ); - } - - /** - * Returns a full channel name containing the system, site and channel name. - */ - public String getChannelDisplayName() - { - StringBuilder sb = new StringBuilder(); - - sb.append( ( mSystem == null ? "-" : mSystem ) ); - sb.append( ( mSite == null ? "-" : mSite ) ); - sb.append( mName ); - - return sb.toString(); - } - - /** - * Returns the tuner channel requirement for this channel when the source - * configuration specifies a tuner and frequency as the source for this - * channel. - * - * If the source configuration is anything else, this method returns null. - */ - public TunerChannel getTunerChannel() - { - TunerChannel retVal = null; - - if( mSourceConfiguration.getSourceType() == SourceType.TUNER ) - { - SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration; - - retVal = new TunerChannel( Type.LOCKED, config.getFrequency(), - mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); - } - else if( mSourceConfiguration.getSourceType() == SourceType.RECORDING ) - { - SourceConfigRecording config = - (SourceConfigRecording)mSourceConfiguration; - - retVal = new TunerChannel( Type.LOCKED, config.getFrequency(), - mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); - } - - return retVal; - } - /** * Returns the owning system for this channel. */ @@ -343,50 +172,78 @@ public class Channel extends Configuration @XmlAttribute( name = "site" ) public String getSite() { - return mSite; + return mSite; } public void setSite( String site ) { mSite = site; } - + public boolean hasSite() { return mSite != null; } + /** + * Returns the name of this channel. + */ + @XmlAttribute( name = "name" ) + public String getName() + { + return mName; + } + + /** + * Sets the name of this channel. + */ + public void setName( String name ) + { + mName = name; + } + /** - * Default display string for this channel -- the channel name. + * Default display string for this channel: SYSTEM_SITE_NAME */ public String toString() { - return mName; + StringBuilder sb = new StringBuilder(); + sb.append( hasSystem() ? getSystem() : "SYSTEM" ); + sb.append( "_" ); + sb.append( hasSite() ? getSite() : "SITE" ); + sb.append( "_" ); + sb.append( getName() ); + + return sb.toString(); } /** * Indicates if this channel is enabled for processing. */ - @XmlAttribute + @XmlAttribute( name = "enabled" ) public boolean getEnabled() { return mEnabled; } /** - * Enables/disables this channel for processing. Automatically starts or - * stops channel processing in response to this change. + * Indicates if this channel will automatically start processing on + * application startup or if this channel is currently processing after + * application startup. + * + * Note: this method is package private and is intended to be managed + * by a co-package central processing manager + * + * @see controller.ChannelProcessingManager */ - public void setEnabled( boolean enabled ) + void setEnabled( boolean enabled ) { mEnabled = enabled; - - enableChanged(); } /** * Returns the alias list that is used by this channel for looking up alias - * values for the various identifiers produced by the decoder. + * values for the various identifiers produced by the decoder(s). */ @XmlElement( name = "alias_list_name" ) public String getAliasListName() @@ -404,7 +261,7 @@ public class Channel extends Configuration } /** - * Gets the decoder configuration used by this channel + * Gets the primary decoder configuration used by this channel */ @XmlElement( name = "decode_configuration" ) public DecodeConfiguration getDecodeConfiguration() @@ -413,7 +270,7 @@ public class Channel extends Configuration } /** - * Sets the decoder configuration used by this channel + * Sets the primary decoder configuration used by this channel */ public void setDecodeConfiguration( DecodeConfiguration config ) { @@ -421,7 +278,8 @@ public class Channel extends Configuration } /** - * Gets the aux decoder configuration used by this channel + * Gets the aux decoder configuration used by this channel. Aux decoders + * operate on demodulated audio */ @XmlElement( name = "aux_decode_configuration" ) public AuxDecodeConfiguration getAuxDecodeConfiguration() @@ -438,7 +296,8 @@ public class Channel extends Configuration } /** - * Returns the source configuration for this channel. + * Returns the source configuration for this channel. A channel source + * identifies where the processing chain will get source sample data from. */ @XmlElement( name = "source_configuration" ) public SourceConfiguration getSourceConfiguration() @@ -452,6 +311,10 @@ public class Channel extends Configuration public void setSourceConfiguration( SourceConfiguration config ) { mSourceConfiguration = config; + + //Clear the tune channel object so that it can be recreated if the + //source configuration changes + mTunerChannel = null; } /** @@ -489,252 +352,41 @@ public class Channel extends Configuration } /** - * Returns the name of this channel. - */ - @XmlAttribute - public String getName() - { - return mName; - } - - /** - * Sets the name of this channel. - */ - public void setName( String name ) - { - mName = name; - } - - /** - * Responds to changes in the enabled setting by starting the processing - * change if the channel is now enabled, or stopping the procesing chain - * if it was already started. - */ - private void enableChanged() - { - if( mEnabled ) - { - start(); - } - else - { - stop(); - } - } - - /** - * Starts the channel processing chain. - */ - private void start() - { - if( mEnabled && mResourceManager != null ) - { - setupChannelState(); - - try - { - setup(); - - mProcessingChain.start(); - } - catch( SourceException se ) - { - //Do nothing, we couldn't get a source for the channel - } - } - } - - private void setupChannelState() - { - if( mChannelState == null ) - { - mChannelState = new ChannelState( mResourceManager - .getThreadPoolManager(), mChannelType ); - - mChannelState.setTrafficChannelTimeout( mTrafficChannelTimeout ); - } - } - - /** - * Sets up the channel and prepares to start processing. Sets up the - * processing chain and requests a source. + * Convenience method to construct a tuner channel object representing a + * tuner or recording source frequency and bandwidth that can be used by + * application components for graphically representing this channel. * - * @throws SourceException - if no source is available or if there is an - * error obtaining a source + * If the source configuration is not a tuner or recording, this method + * returns null. */ - public void setup() throws SourceException - { - setupProcessingChain(); - setupSource(); - } + @XmlTransient + public TunerChannel getTunerChannel() + { + if( mTunerChannel == null ) + { + if( mSourceConfiguration.getSourceType() == SourceType.TUNER ) + { + SourceConfigTuner config = (SourceConfigTuner)mSourceConfiguration; + + mTunerChannel = new TunerChannel( Type.LOCKED, config.getFrequency(), + mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); + } + else if( mSourceConfiguration.getSourceType() == SourceType.RECORDING ) + { + SourceConfigRecording config = + (SourceConfigRecording)mSourceConfiguration; + + mTunerChannel = new TunerChannel( Type.LOCKED, config.getFrequency(), + mDecodeConfiguration.getDecoderType().getChannelBandwidth() ); + } + } + + return mTunerChannel; + } /** - * Setup recording options. Adds RecorderManager as a audio packet listener - * to receive and record decoded audio. - */ - private void setupRecording() - { - List recorders = mRecordConfiguration.getRecorders(); - - if( !recorders.isEmpty() ) - { - if( recorders.contains( RecorderType.AUDIO ) ) - { - mProcessingChain.addAudioPacketListener( - mResourceManager.getRecorderManager() ); - } - - /* Add baseband recorder */ - if( ( recorders.contains( RecorderType.BASEBAND ) && - mChannelType == ChannelType.STANDARD ) ) - { - mProcessingChain.addModule( RecorderManager.getBasebandRecorder( - mResourceManager.getThreadPoolManager(), getChannelName() ) ); - } - - /* Add traffic channel baseband recorder */ - if( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) && - mChannelType == ChannelType.TRAFFIC ) - { - mProcessingChain.addModule( RecorderManager.getBasebandRecorder( - mResourceManager.getThreadPoolManager(), getChannelName() ) ); - } - } - } - - private String getChannelName() - { - StringBuilder sb = new StringBuilder(); - sb.append( mSystem ); - sb.append( "_" ); - sb.append( mSite ); - sb.append( "_" ); - sb.append( mName ); - - return sb.toString(); - } - - private void setupLogging() - { - if( mProcessingChain != null ) - { - String channelName = getChannelName(); - - List loggers = mResourceManager.getEventLogManager() - .getLoggers( mEventLogConfiguration, channelName ); - - if( !loggers.isEmpty() ) - { - mProcessingChain.addModules( loggers ); - } - } - } - - private void setupProcessingChain() - { - if( mProcessingChain == null ) - { - mProcessingChain = new ProcessingChain(); - - /* Get the optional alias list for the decode modules to use */ - AliasList aliasList = mResourceManager.getPlaylistManager() - .getPlayist().getAliasDirectory().getAliasList( mAliasListName ); - - /* Add the channel state as a module */ - mProcessingChain.addModule( mChannelState ); - - /* Processing Modules */ - List modules = DecoderFactory.getModules( mChannelType, - mResourceManager, mDecodeConfiguration, mRecordConfiguration, - mAuxDecodeConfiguration, aliasList, mSystem, - mSite, mName ); - - mProcessingChain.addModules( modules ); - - /* Get message filters for the set of processing modules */ - FilterSet messageFilter = DecoderFactory.getMessageFilters( modules ); - - /* Setup the message activity model and add message listeners */ - mMessageActivityModel = new MessageActivityModel( messageFilter ); - mProcessingChain.addMessageListener( mMessageActivityModel ); - - /* Add audio manager as listener for audio packets */ - mProcessingChain.addAudioPacketListener( - mResourceManager.getAudioManager() ); - - /* Create the call event model to receive call events */ - mCallEventModel = new CallEventModel(); - mProcessingChain.addCallEventListener( mCallEventModel ); - - setupLogging(); - - setupRecording(); - - /* Inject channel metadata that will be inserted into audio packets - * for the recorder manager and streaming */ - mProcessingChain.broadcast( - new Metadata( MetadataType.SYSTEM, mSystem ) ); - mProcessingChain.broadcast( - new Metadata( MetadataType.SITE_ID, mSite ) ); - mProcessingChain.broadcast( - new Metadata( MetadataType.CHANNEL_NAME, mName ) ); - } - } - - /** - * Attempts to obtain a source and apply it to the processing chain. - * - * @throws SourceException - if the processing chain is not setup or if - * there is an error produced while obtaining the source - */ - private void setupSource() throws SourceException - { - if( mProcessingChain != null ) - { - Source source = mResourceManager.getSourceManager() - .getSource( getSourceConfiguration(), getDecodeConfiguration() - .getDecoderType().getChannelBandwidth() ); - - if( source != null ) - { - mProcessingChain.setSource( source ); - } - else - { - throw new SourceException( "Couldn't obtain tuner channel source" ); - } - } - else - { - throw new SourceException( "Cannot obtain source for a null " - + "processing chain - setup the processing chain first" ); - } - } - - /** - * Stops the channel processing chain - */ - private void stop() - { - if( !mEnabled && mProcessingChain != null ) - { - fireChannelEvent( Event.CHANNEL_PROCESSING_STOPPED ); - - mProcessingChain.stop(); - - if( mChannelType == ChannelType.STANDARD ) - { - mProcessingChain.dispose(); - mProcessingChain = null; - } - - mSelected = false; - } - } - - /** - * Indicates if any part of this channel is contained within the - * minimum and maximum frequency values. + * Convenience method to indicate if any part of this channel is contained + * within the minimum and maximum frequency values. */ public boolean isWithin( long minimum, long maximum ) { diff --git a/src/controller/Editor.java b/src/controller/channel/ChannelConfigurationEditor.java similarity index 58% rename from src/controller/Editor.java rename to src/controller/channel/ChannelConfigurationEditor.java index 56411ce1..7b3e1427 100644 --- a/src/controller/Editor.java +++ b/src/controller/channel/ChannelConfigurationEditor.java @@ -1,6 +1,7 @@ /******************************************************************************* + * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -15,37 +16,29 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller; +package controller.channel; import javax.swing.JPanel; -import net.miginfocom.swing.MigLayout; -import controller.channel.ChannelValidationException; - -public abstract class Editor extends JPanel +public abstract class ChannelConfigurationEditor extends JPanel { private static final long serialVersionUID = 1L; - public Editor() - { - setLayout( new MigLayout() ); - } + /** + * Sets or resets the configuration editor components + */ + public abstract void setConfiguration( Channel channel ); + /** + * Saves the contents of the editor components to the configuration + */ public abstract void save(); - public abstract void reset(); - /** - * Override this method to allow the editor to inspect the argument for - * valid configuration + * Validates the contents of the editor components prior to saving the + * content to the configuration. * - * @param editor - any editor - * - * @throws ChannelValidationException if the configuration is not compatible - * with this editor's configuration. Supplies a validation text value for - * display to the user + * @throws ConfigurationValidationException if the content is invalid */ - public void validate( Editor editor ) throws ChannelValidationException - { - } + public abstract void validateConfiguration() throws ConfigurationValidationException; } diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index c9935088..a76902d7 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -19,21 +19,17 @@ package controller.channel; import java.awt.Color; -import java.awt.ComponentOrientation; +import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Collections; -import java.util.List; -import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; +import javax.swing.JPanel; import javax.swing.JTextField; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; import module.decode.AuxDecodeComponentEditor; import module.decode.DecodeComponentEditor; @@ -46,20 +42,21 @@ import org.slf4j.LoggerFactory; import playlist.PlaylistManager; import record.RecordComponentEditor; import source.SourceComponentEditor; +import source.SourceManager; import alias.AliasList; import com.jidesoft.swing.JideTabbedPane; -import controller.Editor; +import controller.channel.ChannelEvent.Event; -public class ChannelEditor extends Editor implements ActionListener +public class ChannelEditor extends JPanel + implements ActionListener, ChannelEventListener { + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( ChannelEditor.class ); - private static final long serialVersionUID = 1L; - private DefaultTreeModel mTreeModel; - private ChannelNode mChannelNode; - + private ChannelConfigurationEditor mNameConfigurationEditor; private DecodeComponentEditor mDecodeEditor; private AuxDecodeComponentEditor mAuxDecodeEditor; private EventLogComponentEditor mEventLogEditor; @@ -70,18 +67,33 @@ public class ChannelEditor extends Editor implements ActionListener private JCheckBox mChannelEnabled = new JCheckBox(); private JComboBox mComboAliasLists; + private Channel mChannel; + private ChannelModel mChannelModel; private PlaylistManager mPlaylistManager; + private SourceManager mSourceManager; - public ChannelEditor( ChannelNode channel, PlaylistManager playlistManager ) + public ChannelEditor( ChannelModel channelModel, + PlaylistManager playlistManager, + SourceManager sourceManager ) { super(); - mChannelNode = channel; - + mChannelModel = channelModel; mPlaylistManager = playlistManager; + mSourceManager = sourceManager; + + mNameConfigurationEditor = new NameConfigurationEditor( + mChannelModel, mPlaylistManager ); initGUI(); } + + public void setChannel( Channel channel ) + { + mNameConfigurationEditor.setConfiguration( channel ); + + mChannel = channel; + } private void initGUI() { @@ -89,65 +101,29 @@ public class ChannelEditor extends Editor implements ActionListener add( new JLabel( "Channel Configuration" ), "span, align center" ); - add( new JLabel( "Enabled:" ) ); + add( new JLabel( "Enabled:" ), "wrap" ); - mChannelEnabled.setSelected( - mChannelNode.getChannel().getEnabled() ); - mChannelEnabled.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - add( mChannelEnabled ); - - add( new JLabel( "Name:" ) ); - - mChannelName = new JTextField( mChannelNode.getChannel().getName() ); - add( mChannelName, "growx,push" ); - - /** - * ComboBox: Alias Lists - */ - add( new JLabel( "Alias List:" ) ); - - mComboAliasLists = new JComboBox(); - - List lists = mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getAliasDirectory().getAliasList(); - - Collections.sort( lists ); - - mComboAliasLists.setModel( new DefaultComboBoxModel( - lists.toArray( new AliasList[ lists.size() ] ) ) ); - - String aliasListName = - mChannelNode.getChannel().getAliasListName(); - - if( aliasListName != null ) - { - AliasList selected = mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mComboAliasLists.setSelectedItem( selected ); - } - - add( mComboAliasLists, "growx,push" ); + add( mNameConfigurationEditor ); JideTabbedPane tabs = new JideTabbedPane(); tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); - mSourceEditor = new SourceComponentEditor( mChannelNode ); - tabs.addTab( "Source", mSourceEditor ); - - mDecodeEditor = new DecodeComponentEditor( mChannelNode, mPlaylistManager ); - tabs.addTab( "Decoder", mDecodeEditor ); - - mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannelNode ); - tabs.addTab( "Aux", mAuxDecodeEditor ); - - mEventLogEditor = new EventLogComponentEditor( mChannelNode ); - tabs.addTab( "Event Log", mEventLogEditor ); - - mRecordEditor = new RecordComponentEditor( mChannelNode ); - tabs.addTab( "Record", mRecordEditor ); + tabs.addTab( "Name/Alias", mNameConfigurationEditor ); +// mSourceEditor = new SourceComponentEditor( mSourceManager, mChannel ); +// tabs.addTab( "Source", mSourceEditor ); +// +// mDecodeEditor = new DecodeComponentEditor( mPlaylistManager, mChannel ); +// tabs.addTab( "Decoder", mDecodeEditor ); +// +// mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannel ); +// tabs.addTab( "Aux", mAuxDecodeEditor ); +// +// mEventLogEditor = new EventLogComponentEditor( mChannel ); +// tabs.addTab( "Event Log", mEventLogEditor ); +// +// mRecordEditor = new RecordComponentEditor( mChannel ); +// tabs.addTab( "Record", mRecordEditor ); add( tabs, "span,grow,push" ); @@ -159,6 +135,12 @@ public class ChannelEditor extends Editor implements ActionListener btnReset.addActionListener( ChannelEditor.this ); add( btnReset, "growx,push" ); } + + @Override + public void channelChanged( ChannelEvent event ) + { + + } @Override public void actionPerformed( ActionEvent e ) @@ -171,109 +153,105 @@ public class ChannelEditor extends Editor implements ActionListener } else if( command.contentEquals( "Reset" ) ) { - reset(); - } - - if( mTreeModel != null ) - { - mTreeModel.nodeChanged( mChannelNode ); + setChannel( mChannel ); } } - @Override public void save() { try { - /** - * Validate the source configuration against the other component - * configuration editors - */ - validate( mSourceEditor.getSourceEditor() ); - - boolean expanded = mChannelNode.getModel().getTree() - .isExpanded( new TreePath( mChannelNode ) ); - - mSourceEditor.save(); - mDecodeEditor.save(); - mAuxDecodeEditor.save(); - mEventLogEditor.save(); - mRecordEditor.save(); - - mChannelNode.getChannel().setName( mChannelName.getText() ); - mChannelNode.getChannel().setEnabled( mChannelEnabled.isSelected() ); - - AliasList selected = mComboAliasLists.getItemAt( - mComboAliasLists.getSelectedIndex() ); - if( selected != null ) - { - mChannelNode.getChannel().setAliasListName( selected.getName() ); - } - - - mChannelNode.save(); - mChannelNode.show(); - - if( expanded ) - { - mChannelNode.getModel().getTree().expandPath( new TreePath( mChannelNode ) ); - } + //Validate each of the configuration editors to check for errors + mNameConfigurationEditor.validateConfiguration(); +// /** +// * Validate the source configuration against the other component +// * configuration editors +// */ +// validate( mSourceEditor.getSourceEditor() ); +// +// mSourceEditor.save(); +// mDecodeEditor.save(); +// mAuxDecodeEditor.save(); +// mEventLogEditor.save(); +// mRecordEditor.save(); +// +// mChannel.setName( mChannelName.getText() ); +// mChannel.setEnabled( mChannelEnabled.isSelected() ); +// +// AliasList selected = mComboAliasLists.getItemAt( +// mComboAliasLists.getSelectedIndex() ); +// if( selected != null ) +// { +// mChannel.setAliasListName( selected.getName() ); +// } } - catch ( ChannelValidationException validationException ) + catch ( ConfigurationValidationException validationException ) { /* ChannelValidationException can be thrown by any of the component * editors. Show the validation error text in a popup menu to the - * user */ + * user and set the focus on the error component */ JOptionPane.showMessageDialog( ChannelEditor.this, validationException.getMessage(), "Channel Configuration Error", JOptionPane.ERROR_MESSAGE ); + + Component component = validationException.getComponent(); + + if( component != null ) + { + component.requestFocus(); + } + + return; } - } - @Override - public void reset() - { - mChannelName.setText( - mChannelNode.getChannel().getName() ); - mChannelName.requestFocus(); - mChannelName.requestFocusInWindow(); + //Save the contents of each editor to the channel configuration + mNameConfigurationEditor.save(); - mChannelEnabled.setSelected( - mChannelNode.getChannel().getEnabled() ); - mChannelEnabled.requestFocus(); - mChannelEnabled.requestFocusInWindow(); - - String aliasListName = - mChannelNode.getChannel().getAliasListName(); - - if( aliasListName != null ) - { - AliasList selected = mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mComboAliasLists.setSelectedItem( selected ); - } - - - mSourceEditor.reset(); - mDecodeEditor.reset(); - mAuxDecodeEditor.reset(); - mEventLogEditor.reset(); - mRecordEditor.reset(); + //Broadcast a channel configuration change event + mChannelModel.broadcast( new ChannelEvent( mChannel, + Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); } - /** - * Validate the specified editor against each of the component editors - */ - @Override - public void validate( Editor editor ) throws ChannelValidationException - { - mSourceEditor.validate( editor ); - mDecodeEditor.validate( editor ); - mAuxDecodeEditor.validate( editor ); - mEventLogEditor.validate( editor ); - mRecordEditor.validate( editor ); - } +// @Override +// public void reset() +// { +// mChannelName.setText( mChannel.getName() ); +// mChannelName.requestFocus(); +// mChannelName.requestFocusInWindow(); +// +// mChannelEnabled.setSelected( mChannel.getEnabled() ); +// mChannelEnabled.requestFocus(); +// mChannelEnabled.requestFocusInWindow(); +// +// String aliasListName = mChannel.getAliasListName(); +// +// if( aliasListName != null ) +// { +// AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() +// .getAliasList( aliasListName ); +// +// mComboAliasLists.setSelectedItem( selected ); +// } +// +// +// mSourceEditor.reset(); +// mDecodeEditor.reset(); +// mAuxDecodeEditor.reset(); +// mEventLogEditor.reset(); +// mRecordEditor.reset(); +// } + +// /** +// * Validate the specified editor against each of the component editors +// */ +// @Override +// public void validate( ChannelConfigurationEditor editor ) throws ConfigurationValidationException +// { +// mSourceEditor.validate( editor ); +// mDecodeEditor.validate( editor ); +// mAuxDecodeEditor.validate( editor ); +// mEventLogEditor.validate( editor ); +// mRecordEditor.validate( editor ); +// } } diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java index 93bb94fc..a4166a2d 100644 --- a/src/controller/channel/ChannelEvent.java +++ b/src/controller/channel/ChannelEvent.java @@ -64,6 +64,8 @@ public class ChannelEvent NOTIFICATION_SELECTION_CHANGE, //Channel's channel-state has been reset NOTIFICATION_STATE_RESET, + //Request to delete the channel + REQUEST_DELETE, //Request to deselect the channel REQUEST_DESELECT, //Request to disable a channel - response will be a PROCESSING_STOP_NOTIFICATION diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java new file mode 100644 index 00000000..1f0d6821 --- /dev/null +++ b/src/controller/channel/ChannelModel.java @@ -0,0 +1,346 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 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 + ******************************************************************************/ +package controller.channel; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; + +import module.decode.DecoderType; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelEvent.Event; + +/** + * Channel Model + */ +public class ChannelModel implements IChannelEventBroadcaster, TableModel +{ + private ChannelSelectionManager mChannelSelectionManager; + private List mChannels = new ArrayList<>(); + private List mTrafficChannels = new ArrayList<>(); + private List mListeners = new CopyOnWriteArrayList<>(); + private List mTableModelListeners = new CopyOnWriteArrayList<>(); + + public ChannelModel() + { + mChannelSelectionManager = new ChannelSelectionManager( this ); + addListener( mChannelSelectionManager ); + } + + /** + * Unmodifiable list of non-traffic channels currently in the model + */ + public List getChannels() + { + return Collections.unmodifiableList( mChannels ); + } + + /** + * Returns a list of unique system values from across the channel set + */ + public List getSystems() + { + List systems = new ArrayList<>(); + + for( Channel channel: mChannels ) + { + if( channel.hasSystem() && !systems.contains( channel.getSystem() ) ) + { + systems.add( channel.getSystem() ); + } + } + + Collections.sort( systems ); + + return systems; + } + + /** + * Returns a list of unique site values for all channels that have a matching + * system value + */ + public List getSites( String system ) + { + List sites = new ArrayList<>(); + + if( system != null ) + { + for( Channel channel: mChannels ) + { + if( channel.hasSystem() && + system.equals( channel.getSystem() ) && + channel.hasSite() && + !sites.contains( channel.getSite() ) ) + { + sites.add( channel.getSite() ); + } + } + } + + Collections.sort( sites ); + + return sites; + } + + /** + * Broadcasts the channel event to all registered listeners + */ + @Override + public void broadcast( ChannelEvent event ) + { + for( ChannelEventListener listener: mListeners ) + { + listener.channelChanged( event ); + } + + if( event.getChannel().getChannelType() == ChannelType.STANDARD ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + case NOTIFICATION_SELECTION_CHANGE: + int index = mChannels.indexOf( event.getChannel() ); + broadcast( new TableModelEvent( this, index ) ); + break; + default: + break; + } + } + + if( event.getEvent() == Event.REQUEST_DELETE ) + { + removeChannel( event.getChannel() ); + } + } + + /** + * Bulk loading of channel list. Each channel is added and a channel add + * event is broadcast. + */ + public void addChannels( List channels ) + { + for( Channel channel: channels ) + { + addChannel( channel ); + } + } + + /** + * Adds the channel to the model and broadcasts a channel add event + */ + public void addChannel( Channel channel ) + { + switch( channel.getChannelType() ) + { + case STANDARD: + mChannels.add( channel ); + + int index = mChannels.size() - 1; + + broadcast( new TableModelEvent( this, index, index, + TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT ) ); + break; + case TRAFFIC: + mTrafficChannels.add( channel ); + break; + default: + break; + } + + broadcast( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) ); + } + + /** + * Removes the channel from the model and broadcasts a channel remove event + */ + public void removeChannel( Channel channel ) + { + switch( channel.getChannelType() ) + { + case STANDARD: + int index = mChannels.indexOf( channel ); + + mChannels.remove( channel ); + + if( index >= 0 ) + { + broadcast( new TableModelEvent( this, index, index, + TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); + } + break; + case TRAFFIC: + mTrafficChannels.remove( channel ); + break; + default: + break; + } + + broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) ); + } + + /** + * Returns a list of channels that fall within the frequency range + * + * @param start frequency of the range + * @param stop frequency of the range + * + * @return list of channels or an empty list if none fall within the range + */ + public List getChannelsInFrequencyRange( long start, long stop ) + { + List channels = new ArrayList<>(); + + for( Channel channel: mChannels ) + { + if( channel.isWithin( start, stop ) ) + { + channels.add( channel ); + } + } + + for( Channel channel: mTrafficChannels ) + { + if( channel.isWithin( start, stop ) ) + { + channels.add( channel ); + } + } + + return channels; + } + + public void addListener( ChannelEventListener listener ) + { + mListeners.add( listener ); + } + + public void removeListener( ChannelEventListener listener ) + { + mListeners.remove( listener ); + } + + //Table Model Interface Methods - standard channels only + @Override + public int getRowCount() + { + return mChannels.size(); + } + + @Override + public int getColumnCount() + { + return 6; + } + + @Override + public String getColumnName( int columnIndex ) + { + switch( columnIndex ) + { + case 0: + return "System"; + case 1: + return "Site"; + case 2: + return "Name"; + case 3: + return "Alias List"; + case 4: + return "Source"; + case 5: + return "Decoder"; + } + + return null; + } + + @Override + public Class getColumnClass( int columnIndex ) + { + return String.class; + } + + @Override + public boolean isCellEditable( int rowIndex, int columnIndex ) + { + return false; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + Channel channel = mChannels.get( rowIndex ); + + switch( columnIndex ) + { + case 0: + return channel.getSystem(); + case 1: + return channel.getSite(); + case 2: + return channel.getName(); + case 3: + return channel.getAliasListName(); + case 4: + return channel.getSourceConfiguration().getDescription(); + case 5: + return channel.getDecodeConfiguration() + .getDecoderType().getShortDisplayString(); + } + + return null; + } + + @Override + public void setValueAt( Object aValue, int rowIndex, int columnIndex ) + { + throw new IllegalArgumentException( "Not yet implemented" ); + } + + private void broadcast( TableModelEvent event ) + { + for( TableModelListener listener: mTableModelListeners ) + { + listener.tableChanged( event ); + } + } + + @Override + public void addTableModelListener( TableModelListener listener ) + { + mTableModelListeners.add( listener ); + } + + @Override + public void removeTableModelListener( TableModelListener listener ) + { + mTableModelListeners.remove( listener ); + } + + public void createChannel( DecoderType decoderType, long frequency ) + { + throw new UnsupportedOperationException( "Not yet implemented" ); + } +} diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java index acaa0222..ca8e5b37 100644 --- a/src/controller/channel/ChannelNode.java +++ b/src/controller/channel/ChannelNode.java @@ -27,6 +27,8 @@ import javax.swing.JPopupMenu; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.PlaylistManager; +import source.SourceManager; import source.tuner.TunerChannel; import controller.ConfigurableNode; import controller.site.SiteNode; @@ -43,10 +45,22 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene private final static Logger mLog = LoggerFactory.getLogger( ChannelNode.class ); private static DecimalFormat sFORMAT = new DecimalFormat( "0.0000" ); + + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private SourceManager mSourceManager; - public ChannelNode( Channel channel ) + public ChannelNode( Channel channel, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SourceManager sourceManager ) { - super( channel ); + super( playlistManager, channel ); + + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mSourceManager = sourceManager; } public void init() @@ -69,9 +83,6 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene /* Add this node as listener to receive changes from underlying channel */ // getChannel().addListener( this ); - /* Add the resource manager to the channel so that the channel - * can provide channel change events to all system resources */ - getChannel().setResourceManager( getModel().getResourceManager() ); } /** @@ -109,7 +120,7 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene { if( getChannel().getEnabled() ) { - if( getChannel().isProcessing() ) + if( getChannel().getEnabled() ) { return Color.GREEN; } @@ -125,9 +136,7 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene @Override public JPanel getEditor() { - return new ChannelEditor( this, - getChannel().getResourceManager() == null ? null : - getChannel().getResourceManager().getPlaylistManager() ); + return new OldChannelEditor( getChannel(), mChannelModel, getPlaylistManager(), mSourceManager ); } public Channel getChannel() @@ -158,9 +167,9 @@ public class ChannelNode extends ConfigurableNode implements ChannelEventListene { JPopupMenu popupMenu = new JPopupMenu(); - JMenu channelMenu = ChannelUtils.getContextMenu( - getModel().getResourceManager().getPlaylistManager(), - getChannel(), getModel().getTree() ); + JMenu channelMenu = ChannelUtils.getContextMenu( mChannelModel, + mChannelProcessingManager, getPlaylistManager(), getChannel(), + getModel().getTree() ); if( channelMenu != null ) { diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java new file mode 100644 index 00000000..9bdb727e --- /dev/null +++ b/src/controller/channel/ChannelProcessingManager.java @@ -0,0 +1,301 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ + +package controller.channel; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; + +import message.Message; +import module.Module; +import module.ProcessingChain; +import module.decode.DecoderFactory; +import module.decode.event.MessageActivityModel; +import module.log.EventLogManager; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistManager; +import record.RecorderManager; +import record.RecorderType; +import sample.Listener; +import source.Source; +import source.SourceException; +import source.SourceManager; +import audio.AudioPacket; +import audio.metadata.Metadata; +import audio.metadata.MetadataType; +import controller.ThreadPoolManager; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelEvent.Event; +import filter.FilterSet; + +public class ChannelProcessingManager implements ChannelEventListener +{ + private final static Logger mLog = LoggerFactory.getLogger( ChannelProcessingManager.class ); + + private Map mProcessingChains = new HashMap<>(); + + private List> mAudioPacketListeners = new CopyOnWriteArrayList<>(); + private List> mMessageListeners = new CopyOnWriteArrayList<>(); + + private ChannelModel mChannelModel; + private EventLogManager mEventLogManager; + private PlaylistManager mPlaylistManager; + private RecorderManager mRecorderManager; + private SourceManager mSourceManager; + private ThreadPoolManager mThreadPoolManager; + + public ChannelProcessingManager( ChannelModel channelModel, + EventLogManager eventLogManager, + PlaylistManager playlistManager, + RecorderManager recorderManager, + SourceManager sourceManager, + ThreadPoolManager threadPoolManager ) + { + mChannelModel = channelModel; + mEventLogManager = eventLogManager; + mPlaylistManager = playlistManager; + mRecorderManager = recorderManager; + mSourceManager = sourceManager; + mThreadPoolManager = threadPoolManager; + + mChannelModel.addListener( this ); + } + + /** + * Indicates if a processing chain is constructed for the channel and that + * the processing chain is currently processing. + */ + public boolean isProcessing( Channel channel ) + { + if( mProcessingChains.containsKey( channel.getChannelID() ) ) + { + return mProcessingChains.get( channel.getChannelID() ).isProcessing(); + } + + return false; + } + + /** + * Returns the current processing chain associated with the channel, or + * null if a processing chain is not currently setup for the channel + */ + public ProcessingChain getProcessingChain( Channel channel ) + { + return mProcessingChains.get( channel.getChannelID() ); + } + + @Override + public void channelChanged( ChannelEvent event ) + { + Channel channel = event.getChannel(); + + switch( event.getEvent() ) + { + case REQUEST_ENABLE: + if( !mProcessingChains.containsKey( channel.getChannelID() ) || + !mProcessingChains.get( channel.getChannelID() ).isProcessing() ) + { + startProcessing( channel ); + } + break; + case REQUEST_DISABLE: + if( channel.getEnabled() ) + { + switch( channel.getChannelType() ) + { + case STANDARD: + stopProcessing( channel, true ); + break; + case TRAFFIC: + //Don't remove traffic channel processing chains + //until explicitly deleted, so that we can reuse them + stopProcessing( channel, false ); + default: + break; + } + } + case NOTIFICATION_DELETE: + if( channel.getEnabled() ) + { + stopProcessing( channel, true ); + } + break; + default: + break; + } + } + + private void startProcessing( Channel channel ) + { + ProcessingChain processingChain = mProcessingChains.get( channel.getChannelID() ); + + //If we're already processing, ignore the request + if( processingChain != null && processingChain.isProcessing() ) + { + return; + } + + //Ensure that we can get a source before we construct a new processing chain + Source source = null; + + try + { + source = mSourceManager.getSource( channel.getSourceConfiguration(), + channel.getDecodeConfiguration().getDecoderType().getChannelBandwidth() ); + } + catch( SourceException se ) + { + mLog.debug( "Error obtaining source for channel [" + channel.getName() + "]", se ); + } + + if( source == null ) + { + channel.setEnabled( false ); + + mChannelModel.broadcast( + new ChannelEvent( channel, Event.NOTIFICATION_ENABLE_REJECTED ) ); + + return; + } + + if( processingChain == null ) + { + processingChain = new ProcessingChain( mThreadPoolManager, + channel.getChannelType() ); + + /* Register global listeners */ + for( Listener listener: mAudioPacketListeners ) + { + processingChain.addAudioPacketListener( listener ); + } + + for( Listener listener: mMessageListeners ) + { + processingChain.addMessageListener( listener ); + } + + /* Processing Modules */ + List modules = DecoderFactory.getModules( mChannelModel, + this, mPlaylistManager, channel ); + processingChain.addModules( modules ); + + /* Setup message activity model with filtering */ + FilterSet messageFilter = DecoderFactory.getMessageFilters( modules ); + MessageActivityModel messageModel = new MessageActivityModel( messageFilter ); + processingChain.setMessageActivityModel( messageModel ); + + /* Setup event logging */ + List loggers = mEventLogManager.getLoggers( + channel.getEventLogConfiguration(), channel.getName() ); + + if( !loggers.isEmpty() ) + { + processingChain.addModules( loggers ); + } + + /* Setup recorders */ + List recorders = channel.getRecordConfiguration().getRecorders(); + + if( !recorders.isEmpty() ) + { + if( recorders.contains( RecorderType.AUDIO ) ) + { + processingChain.addAudioPacketListener( mRecorderManager ); + } + + /* Add baseband recorder */ + if( ( recorders.contains( RecorderType.BASEBAND ) && + channel.getChannelType() == ChannelType.STANDARD ) ) + { + processingChain.addModule( mRecorderManager.getBasebandRecorder( + channel.toString() ) ); + } + + /* Add traffic channel baseband recorder */ + if( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) && + channel.getChannelType() == ChannelType.TRAFFIC ) + { + processingChain.addModule( mRecorderManager + .getBasebandRecorder( channel.toString() ) ); + } + } + + /* Inject channel metadata that will be inserted into audio packets + * for the recorder manager and streaming */ + processingChain.broadcast( + new Metadata( MetadataType.SYSTEM, channel.getSystem() ) ); + processingChain.broadcast( + new Metadata( MetadataType.SITE_ID, channel.getSite() ) ); + processingChain.broadcast( + new Metadata( MetadataType.CHANNEL_NAME, channel.getName() ) ); + } + + processingChain.setSource( source ); + processingChain.start(); + + channel.setEnabled( true ); + + mChannelModel.broadcast( + new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_START ) ); + } + + private void stopProcessing( Channel channel, boolean remove ) + { + mChannelModel.broadcast( + new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) ); + } + + /** + * Adds a message listener that will be added to all channels to receive + * any messages. + */ + public void addAudioPacketListener( Listener listener ) + { + mAudioPacketListeners.add( listener ); + } + + /** + * Removes a message listener. + */ + public void removeAudioPacketListener( Listener listener ) + { + mAudioPacketListeners.remove( listener ); + } + + /** + * Adds a message listener that will be added to all channels to receive + * any messages. + */ + public void addMessageListener( Listener listener ) + { + mMessageListeners.add( listener ); + } + + /** + * Removes a message listener. + */ + public void removeMessageListener( Listener listener ) + { + mMessageListeners.remove( listener ); + } +} diff --git a/src/controller/channel/ChannelSelectionManager.java b/src/controller/channel/ChannelSelectionManager.java new file mode 100644 index 00000000..65ac9b91 --- /dev/null +++ b/src/controller/channel/ChannelSelectionManager.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015 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 + ******************************************************************************/ +package controller.channel; + +import controller.channel.ChannelEvent.Event; + +public class ChannelSelectionManager implements ChannelEventListener +{ + private IChannelEventBroadcaster mChannelEventBroadcaster; + private Channel mSelectedChannel = null; + + /** + * Manages channel selection state to ensure that only one channel is ever + * in a selected state. + */ + public ChannelSelectionManager( IChannelEventBroadcaster broadcaster ) + { + mChannelEventBroadcaster = broadcaster; + } + + @Override + public void channelChanged( ChannelEvent event ) + { + switch( event.getEvent() ) + { + case REQUEST_SELECT: + if( mSelectedChannel != null ) + { + mSelectedChannel.setSelected( false ); + mChannelEventBroadcaster.broadcast( new ChannelEvent( mSelectedChannel, + Event.NOTIFICATION_SELECTION_CHANGE ) ); + mSelectedChannel = null; + } + + mSelectedChannel = event.getChannel(); + mSelectedChannel.setSelected( true ); + mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(), + Event.NOTIFICATION_SELECTION_CHANGE ) ); + break; + case NOTIFICATION_PROCESSING_STOP: + case REQUEST_DESELECT: + if( mSelectedChannel != null && + mSelectedChannel == event.getChannel() ) + { + mSelectedChannel.setSelected( false ); + mSelectedChannel = null; + mChannelEventBroadcaster.broadcast( new ChannelEvent( event.getChannel(), + Event.NOTIFICATION_SELECTION_CHANGE ) ); + } + break; + default: + break; + } + } + +} diff --git a/src/controller/channel/ChannelUtils.java b/src/controller/channel/ChannelUtils.java index f4fd2bf1..9a716ea3 100644 --- a/src/controller/channel/ChannelUtils.java +++ b/src/controller/channel/ChannelUtils.java @@ -9,10 +9,11 @@ import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JSeparator; +import controller.channel.ChannelEvent.Event; +import module.ProcessingChain; import module.decode.event.ActivitySummaryFrame; import module.decode.state.DecoderState; import playlist.PlaylistManager; -import controller.channel.ChannelEvent.Event; public class ChannelUtils { @@ -23,7 +24,9 @@ public class ChannelUtils /** * Creates a context menu for the channel argument */ - public static JMenu getContextMenu( final PlaylistManager playlistManager, + public static JMenu getContextMenu( final ChannelModel channelModel, + final ChannelProcessingManager channelProcessingManager, + final PlaylistManager playlistManager, final Channel channel, final Component anchor ) { @@ -39,12 +42,8 @@ public class ChannelUtils @Override public void actionPerformed( ActionEvent e ) { - channel.setEnabled( DISABLED ); - - if( playlistManager != null ) - { - playlistManager.save(); - } + channelModel.broadcast( + new ChannelEvent( channel, Event.REQUEST_DISABLE ) ); } } ); @@ -62,10 +61,14 @@ public class ChannelUtils { StringBuilder sb = new StringBuilder(); - for( DecoderState decoderState: channel - .getProcessingChain().getDecoderStates() ) + ProcessingChain chain = channelProcessingManager.getProcessingChain( channel ); + + if( chain != null ) { - sb.append( decoderState.getActivitySummary() ); + for( DecoderState decoderState: chain.getDecoderStates() ) + { + sb.append( decoderState.getActivitySummary() ); + } } new ActivitySummaryFrame( sb.toString(), anchor ); @@ -82,12 +85,8 @@ public class ChannelUtils @Override public void actionPerformed( ActionEvent e ) { - channel.setEnabled( ENABLED ); - - if( playlistManager != null ) - { - playlistManager.save(); - } + channelModel.broadcast( + new ChannelEvent( channel, Event.REQUEST_ENABLE ) ); } } ); @@ -108,16 +107,8 @@ public class ChannelUtils if( response == JOptionPane.YES_OPTION ) { - /* Disable the channel */ - channel.setEnabled( DISABLED ); - - /* Broadcast channel deleted event */ -// channel.fireChannelEvent( Event.NOTIFICATION_DELETE ); - - if( playlistManager != null ) - { - playlistManager.save(); - } + channelModel.broadcast( + new ChannelEvent( channel, Event.REQUEST_DELETE ) ); } } } ); diff --git a/src/controller/channel/ChannelValidationException.java b/src/controller/channel/ChannelValidationException.java deleted file mode 100644 index 53422a11..00000000 --- a/src/controller/channel/ChannelValidationException.java +++ /dev/null @@ -1,11 +0,0 @@ -package controller.channel; - -public class ChannelValidationException extends Exception -{ - private static final long serialVersionUID = 1L; - - public ChannelValidationException( String validationMessage ) - { - super( validationMessage ); - } -} diff --git a/src/controller/channel/ConfigurationValidationException.java b/src/controller/channel/ConfigurationValidationException.java new file mode 100644 index 00000000..a61862c5 --- /dev/null +++ b/src/controller/channel/ConfigurationValidationException.java @@ -0,0 +1,23 @@ +package controller.channel; + +import java.awt.Component; + +public class ConfigurationValidationException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Component mComponent; + + public ConfigurationValidationException( Component component, + String validationMessage ) + { + super( validationMessage ); + + mComponent = component; + } + + public Component getComponent() + { + return mComponent; + } +} diff --git a/src/buffer/ComplexByteBufferListener.java b/src/controller/channel/IChannelEventBroadcaster.java similarity index 77% rename from src/buffer/ComplexByteBufferListener.java rename to src/controller/channel/IChannelEventBroadcaster.java index efdb3a1a..ef7c89fd 100644 --- a/src/buffer/ComplexByteBufferListener.java +++ b/src/controller/channel/IChannelEventBroadcaster.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2016 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 @@ -15,9 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package buffer; +package controller.channel; -public interface ComplexByteBufferListener +/** + * Provides channel event broadcasting capability. + */ +public interface IChannelEventBroadcaster { - public void receive( ComplexByteBuffer buffer ); + /** + * Broadcasts the channel event + */ + public void broadcast( ChannelEvent event ); } diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java new file mode 100644 index 00000000..c7c171ec --- /dev/null +++ b/src/controller/channel/NameConfigurationEditor.java @@ -0,0 +1,221 @@ +package controller.channel; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collections; +import java.util.List; + +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.miginfocom.swing.MigLayout; +import playlist.PlaylistManager; +import alias.AliasList; + +public class NameConfigurationEditor extends ChannelConfigurationEditor +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class ); + + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); + + private ChannelModel mChannelModel; + private PlaylistManager mPlaylistManager; + + private JTextField mChannelName = new JTextField( "Please select a channel to edit" ); + private JComboBox mSystemNameCombo = new JComboBox<>( EMPTY_MODEL ); + private JComboBox mSiteNameCombo = new JComboBox<>( EMPTY_MODEL ); + private JComboBox mAliasListCombo; + + private Channel mChannel; + + public NameConfigurationEditor( ChannelModel model, + PlaylistManager playlistManager ) + { + mChannelModel = model; + mPlaylistManager = playlistManager; + + setLayout( new MigLayout( "fill,wrap 2", "[right][]", "[][][][grow]" ) ); + + add( new JLabel( "Name:" ) ); + add( mChannelName, "growx" ); + + mSystemNameCombo.setEditable( true ); + mSystemNameCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Object selected = mSystemNameCombo.getSelectedItem(); + + if( selected != null ) + { + String system = (String)selected; + + List sites = mChannelModel.getSites( system ); + + if( !sites.contains( mChannel.getSite() ) ) + { + sites.add( mChannel.getSite() ); + } + + if( sites.isEmpty() ) + { + mSiteNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSiteNameCombo.setModel( new DefaultComboBoxModel( + sites.toArray( new String[ sites.size() ] ) ) );; + } + + mSiteNameCombo.setSelectedItem( mChannel.getSite() ); + } + } + } ); + + add( new JLabel( "System:" ) ); + add( mSystemNameCombo, "growx" ); + + mSiteNameCombo.setEditable( true ); + + add( new JLabel( "Site:" ) ); + add( mSiteNameCombo, "growx" ); + + /** + * ComboBox: Alias Lists + */ + add( new JLabel( "Alias List:" ) ); + + mAliasListCombo = new JComboBox(); + + List lists = mPlaylistManager.getPlayist() + .getAliasDirectory().getAliasList(); + + Collections.sort( lists ); + + mAliasListCombo.setModel( new DefaultComboBoxModel( + lists.toArray( new AliasList[ lists.size() ] ) ) ); + + add( mAliasListCombo, "growx" ); + + add( new JLabel() ); + + final JButton aliasListButton = new JButton( "New Alias List ..." ); + + aliasListButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + String name = JOptionPane.showInputDialog( aliasListButton, + "Please enter an Alias List name:" ); + + if( name != null && !name.isEmpty() ) + { + AliasList aliasList = new AliasList( name ); + + mPlaylistManager.getPlayist().getAliasDirectory() + .addAliasList( aliasList ); + + mPlaylistManager.save(); + + List lists = mPlaylistManager.getPlayist() + .getAliasDirectory().getAliasList(); + + Collections.sort( lists ); + + mAliasListCombo.setModel( new DefaultComboBoxModel( + lists.toArray( new AliasList[ lists.size() ] ) ) ); + + mAliasListCombo.setSelectedItem( aliasList ); + } + } + } ); + + add( aliasListButton, "growx" ); + } + + @Override + public void save() + { + if( mChannel != null ) + { + mChannel.setName( mChannelName.getText() ); + + Object system = mSystemNameCombo.getSelectedItem(); + mChannel.setSystem( ( system == null ? null : (String)system ) ); + + Object site = mSiteNameCombo.getSelectedItem(); + mChannel.setSystem( ( site == null ? null : (String)site ) ); + } + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + //System and site can be null, but we must (should?) have a channel name + String name = mChannelName.getText(); + + if( name == null || name.isEmpty() ) + { + throw new ConfigurationValidationException( + mChannelName, "Channel name cannot be empty" ); + } + } + + @Override + public void setConfiguration( Channel channel ) + { + mChannel = channel; + + mChannelName.setText( channel.getName() ); + + List systems = mChannelModel.getSystems(); + + if( systems.isEmpty() ) + { + mSystemNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSystemNameCombo.setModel( new DefaultComboBoxModel( + systems.toArray( new String[ systems.size() ] ) ) );; + } + + mSystemNameCombo.setSelectedItem( channel.getSystem() ); + + List sites = mChannelModel.getSites( channel.getSystem() ); + + if( sites.isEmpty() ) + { + mSiteNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSiteNameCombo.setModel( new DefaultComboBoxModel( + sites.toArray( new String[ sites.size() ] ) ) );; + } + + mSiteNameCombo.setSelectedItem( channel.getSite() ); + + String aliasListName = mChannel.getAliasListName(); + + if( aliasListName != null ) + { + AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() + .getAliasList( aliasListName ); + + mAliasListCombo.setSelectedItem( selected ); + } + } +} diff --git a/src/controller/channel/OldChannelEditor.java b/src/controller/channel/OldChannelEditor.java new file mode 100644 index 00000000..f4f317f8 --- /dev/null +++ b/src/controller/channel/OldChannelEditor.java @@ -0,0 +1,260 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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 + ******************************************************************************/ +package controller.channel; + + +import java.awt.Color; +import java.awt.ComponentOrientation; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Collections; +import java.util.List; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JTextField; + +import module.decode.AuxDecodeComponentEditor; +import module.decode.DecodeComponentEditor; +import module.log.EventLogComponentEditor; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistManager; +import record.RecordComponentEditor; +import source.SourceComponentEditor; +import source.SourceManager; +import alias.AliasList; + +import com.jidesoft.swing.JideTabbedPane; + +public class OldChannelEditor extends ChannelConfigurationEditor implements ActionListener +{ + private final static Logger mLog = LoggerFactory.getLogger( OldChannelEditor.class ); + + private static final long serialVersionUID = 1L; + + private DecodeComponentEditor mDecodeEditor; + private AuxDecodeComponentEditor mAuxDecodeEditor; + private EventLogComponentEditor mEventLogEditor; + private RecordComponentEditor mRecordEditor; + private SourceComponentEditor mSourceEditor; + + private JTextField mChannelName; + private JCheckBox mChannelEnabled = new JCheckBox(); + private JComboBox mComboAliasLists; + + private Channel mChannel; + private ChannelModel mChannelModel; + private PlaylistManager mPlaylistManager; + private SourceManager mSourceManager; + + public OldChannelEditor( Channel channel, + ChannelModel channelModel, + PlaylistManager playlistManager, + SourceManager sourceManager ) + { + super(); + + mChannel = channel; + mChannelModel = channelModel; + mPlaylistManager = playlistManager; + mSourceManager = sourceManager; + + initGUI(); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) ); + + add( new JLabel( "Channel Configuration" ), "span, align center" ); + + add( new JLabel( "Enabled:" ) ); + + mChannelEnabled.setSelected( mChannel.getEnabled() ); + mChannelEnabled.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); + add( mChannelEnabled ); + + add( new JLabel( "Name:" ) ); + + mChannelName = new JTextField( mChannel.getName() ); + add( mChannelName, "growx,push" ); + + /** + * ComboBox: Alias Lists + */ + add( new JLabel( "Alias List:" ) ); + + mComboAliasLists = new JComboBox(); + + List lists = mPlaylistManager.getPlayist() + .getAliasDirectory().getAliasList(); + + Collections.sort( lists ); + + mComboAliasLists.setModel( new DefaultComboBoxModel( + lists.toArray( new AliasList[ lists.size() ] ) ) ); + + String aliasListName = mChannel.getAliasListName(); + + if( aliasListName != null ) + { + AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() + .getAliasList( aliasListName ); + + mComboAliasLists.setSelectedItem( selected ); + } + + add( mComboAliasLists, "growx,push" ); + + JideTabbedPane tabs = new JideTabbedPane(); + tabs.setFont( this.getFont() ); + tabs.setForeground( Color.BLACK ); + + mSourceEditor = new SourceComponentEditor( mSourceManager, mChannel ); + tabs.addTab( "Source", mSourceEditor ); + + mDecodeEditor = new DecodeComponentEditor( mPlaylistManager, mChannel ); + tabs.addTab( "Decoder", mDecodeEditor ); + + mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannel ); + tabs.addTab( "Aux", mAuxDecodeEditor ); + + mEventLogEditor = new EventLogComponentEditor( mChannel ); + tabs.addTab( "Event Log", mEventLogEditor ); + + mRecordEditor = new RecordComponentEditor( mChannel ); + tabs.addTab( "Record", mRecordEditor ); + + add( tabs, "span,grow,push" ); + + JButton btnSave = new JButton( "Save" ); + btnSave.addActionListener( OldChannelEditor.this ); + add( btnSave, "growx,push" ); + + JButton btnReset = new JButton( "Reset" ); + btnReset.addActionListener( OldChannelEditor.this ); + add( btnReset, "growx,push" ); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + String command = e.getActionCommand(); + + if( command.contentEquals( "Save" ) ) + { + save(); + } + else if( command.contentEquals( "Reset" ) ) + { +// reset(); + } + } + + @Override + public void save() + { +// try +// { +// /** +// * Validate the source configuration against the other component +// * configuration editors +// */ +// validate( mSourceEditor.getSourceEditor() ); +// +// mSourceEditor.save(); +// mDecodeEditor.save(); +// mAuxDecodeEditor.save(); +// mEventLogEditor.save(); +// mRecordEditor.save(); +// +// mChannel.setName( mChannelName.getText() ); +// mChannel.setEnabled( mChannelEnabled.isSelected() ); +// +// AliasList selected = mComboAliasLists.getItemAt( +// mComboAliasLists.getSelectedIndex() ); +// if( selected != null ) +// { +// mChannel.setAliasListName( selected.getName() ); +// } +// } +// catch ( ConfigurationValidationException validationException ) +// { +// /* ChannelValidationException can be thrown by any of the component +// * editors. Show the validation error text in a popup menu to the +// * user */ +// JOptionPane.showMessageDialog( OldChannelEditor.this, +// validationException.getMessage(), +// "Channel Configuration Error", +// JOptionPane.ERROR_MESSAGE ); +// } + } + +// @Override +// public void reset() +// { +// mChannelName.setText( mChannel.getName() ); +// mChannelName.requestFocus(); +// mChannelName.requestFocusInWindow(); +// +// mChannelEnabled.setSelected( mChannel.getEnabled() ); +// mChannelEnabled.requestFocus(); +// mChannelEnabled.requestFocusInWindow(); +// +// String aliasListName = mChannel.getAliasListName(); +// +// if( aliasListName != null ) +// { +// AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() +// .getAliasList( aliasListName ); +// +// mComboAliasLists.setSelectedItem( selected ); +// } +// +// +// mSourceEditor.reset(); +// mDecodeEditor.reset(); +// mAuxDecodeEditor.reset(); +// mEventLogEditor.reset(); +// mRecordEditor.reset(); +// } + + @Override + public void setConfiguration( Channel channel ) + { + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + mSourceEditor.validate(); + mDecodeEditor.validate(); + mAuxDecodeEditor.validate(); + mEventLogEditor.validate(); + mRecordEditor.validate(); + } + +} diff --git a/src/controller/channel/map/ChannelMapListNode.java b/src/controller/channel/map/ChannelMapListNode.java index 208e22a4..9259bba3 100644 --- a/src/controller/channel/map/ChannelMapListNode.java +++ b/src/controller/channel/map/ChannelMapListNode.java @@ -23,15 +23,16 @@ import java.awt.event.ActionListener; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import controller.ConfigurableNode; public class ChannelMapListNode extends ConfigurableNode { private static final long serialVersionUID = 1L; - public ChannelMapListNode( ChannelMapList list ) + public ChannelMapListNode( PlaylistManager playlistManager, ChannelMapList list ) { - super( list ); + super( playlistManager, list ); } public ChannelMapList getChannelMapList() @@ -43,7 +44,7 @@ public class ChannelMapListNode extends ConfigurableNode { for( ChannelMap channelMap: getChannelMapList().getChannelMap() ) { - ChannelMapNode node = new ChannelMapNode( channelMap ); + ChannelMapNode node = new ChannelMapNode( getPlaylistManager(), channelMap ); getModel().addNode( node, ChannelMapListNode.this, getChildCount() ); @@ -72,7 +73,7 @@ public class ChannelMapListNode extends ConfigurableNode getChannelMapList().addChannelMap( channelMap ); - getModel().addNode( new ChannelMapNode( channelMap ), + getModel().addNode( new ChannelMapNode( getPlaylistManager(), channelMap ), ChannelMapListNode.this, ChannelMapListNode.this.getChildCount() ); diff --git a/src/controller/channel/map/ChannelMapNode.java b/src/controller/channel/map/ChannelMapNode.java index 7a0712e0..ef6c6bfe 100644 --- a/src/controller/channel/map/ChannelMapNode.java +++ b/src/controller/channel/map/ChannelMapNode.java @@ -25,15 +25,16 @@ import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; import controller.ConfigurableNode; public class ChannelMapNode extends ConfigurableNode { private static final long serialVersionUID = 1L; - public ChannelMapNode( ChannelMap ChannelMap ) + public ChannelMapNode( PlaylistManager playlistManager, ChannelMap ChannelMap ) { - super( ChannelMap ); + super( playlistManager, ChannelMap ); } public void init() diff --git a/src/controller/site/SiteNode.java b/src/controller/site/SiteNode.java index b1184ab3..65ad140f 100644 --- a/src/controller/site/SiteNode.java +++ b/src/controller/site/SiteNode.java @@ -29,26 +29,43 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.tree.DefaultMutableTreeNode; +import playlist.PlaylistManager; +import source.SourceManager; import controller.ConfigurableNode; import controller.channel.Channel; +import controller.channel.ChannelModel; import controller.channel.ChannelNode; +import controller.channel.ChannelProcessingManager; import controller.system.System; import controller.system.SystemNode; public class SiteNode extends ConfigurableNode { private static final long serialVersionUID = 1L; + + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private SourceManager mSourceManager; - public SiteNode( Site site ) + public SiteNode( Site site, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SourceManager sourceManager ) { - super( site ); + super( playlistManager, site ); + + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mSourceManager = sourceManager; } public void init() { for( Channel channel: getSite().getChannel() ) { - ChannelNode node = new ChannelNode( channel ); + ChannelNode node = new ChannelNode( channel, mChannelModel, + mChannelProcessingManager, getPlaylistManager(), mSourceManager ); getModel().insertNodeInto( node, SiteNode.this, getChildCount() ); @@ -130,7 +147,8 @@ public class SiteNode extends ConfigurableNode getSite().addChannel( channel ); - ChannelNode node = new ChannelNode( channel ); + ChannelNode node = new ChannelNode( channel, mChannelModel, + mChannelProcessingManager, getPlaylistManager(), mSourceManager ); getModel().addNode( node, SiteNode.this, @@ -139,11 +157,6 @@ public class SiteNode extends ConfigurableNode sort(); save(); node.show(); - - /* Give channel reference to the resource manager and the - * channel will register the listeners and send a channel - * add event */ - channel.setResourceManager( getModel().getResourceManager() ); } } ); diff --git a/src/controller/system/SystemListNode.java b/src/controller/system/SystemListNode.java index 4629133d..d0e5b3a9 100644 --- a/src/controller/system/SystemListNode.java +++ b/src/controller/system/SystemListNode.java @@ -23,15 +23,31 @@ import java.awt.event.ActionListener; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import playlist.PlaylistManager; +import source.SourceManager; import controller.ConfigurableNode; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; public class SystemListNode extends ConfigurableNode { private static final long serialVersionUID = 1L; - public SystemListNode( SystemList list ) + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private SourceManager mSourceManager; + + public SystemListNode( SystemList list, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SourceManager sourceManager ) { - super( list ); + super( playlistManager, list ); + + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mSourceManager = sourceManager; } public SystemList getSystemList() @@ -43,7 +59,8 @@ public class SystemListNode extends ConfigurableNode { for( System system: getSystemList().getSystem() ) { - SystemNode node = new SystemNode( system ); + SystemNode node = new SystemNode( system, mChannelModel, + mChannelProcessingManager, getPlaylistManager(), mSourceManager ); getModel().addNode( node, SystemListNode.this, getChildCount() ); @@ -72,9 +89,9 @@ public class SystemListNode extends ConfigurableNode getSystemList().addSystem( system ); - getModel().addNode( new SystemNode( system ), - SystemListNode.this, - SystemListNode.this.getChildCount() ); + getModel().addNode( new SystemNode( system, mChannelModel, + mChannelProcessingManager, getPlaylistManager(), mSourceManager ), + SystemListNode.this, SystemListNode.this.getChildCount() ); } } ); diff --git a/src/controller/system/SystemNode.java b/src/controller/system/SystemNode.java index a9c4e3b9..39f28214 100644 --- a/src/controller/system/SystemNode.java +++ b/src/controller/system/SystemNode.java @@ -29,7 +29,11 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.tree.DefaultMutableTreeNode; +import playlist.PlaylistManager; +import source.SourceManager; import controller.ConfigurableNode; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; import controller.site.Site; import controller.site.SiteNode; @@ -37,16 +41,29 @@ public class SystemNode extends ConfigurableNode { private static final long serialVersionUID = 1L; - public SystemNode( System system ) + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private SourceManager mSourceManager; + + public SystemNode( System system, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SourceManager sourceManager ) { - super( system ); + super( playlistManager, system ); + + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mSourceManager = sourceManager; } public void init() { for( Site site: getSystem().getSite() ) { - SiteNode node = new SiteNode( site ); + SiteNode node = new SiteNode( site, mChannelModel, + mChannelProcessingManager, getPlaylistManager(), mSourceManager ); getModel().insertNodeInto( node, SystemNode.this, getChildCount() ); @@ -133,7 +150,8 @@ public class SystemNode extends ConfigurableNode Site site = new Site(); getSystem().addSite( site ); - SiteNode node = new SiteNode( site ); + SiteNode node = new SiteNode( site, mChannelModel, + mChannelProcessingManager, getPlaylistManager(), mSourceManager ); getModel().addNode( node, SystemNode.this, diff --git a/src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java b/src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java new file mode 100644 index 00000000..308b4ed7 --- /dev/null +++ b/src/dsp/filter/dc/DCRemovalFilter_RealBuffer.java @@ -0,0 +1,60 @@ +package dsp.filter.dc; + +import sample.Listener; +import sample.real.RealBuffer; + +public class DCRemovalFilter_RealBuffer implements Listener +{ + private Listener mListener; + /** + * Removes DC bias present across the samples in the buffer by calculating + * the mean DC component and subtracting that component from each of the + * samples. + */ + public DCRemovalFilter_RealBuffer() + { + } + + public void dispose() + { + mListener = null; + } + + public RealBuffer filter( RealBuffer buffer ) + { + double accumulator = 0.0d; + + for( float sample: buffer.getSamples() ) + { + accumulator += sample; + } + + float mean = (float)( accumulator / (double)buffer.getSamples().length ); + + for( float sample: buffer.getSamples() ) + { + sample -= mean; + } + + return buffer; + } + + @Override + public void receive( RealBuffer buffer ) + { + if( mListener != null ) + { + mListener.receive( filter( buffer ) ); + } + } + + public void setListener( Listener listener ) + { + mListener = listener; + } + + public void removeListener() + { + mListener = null; + } +} diff --git a/src/dsp/psk/LSMDemodulator.java b/src/dsp/psk/LSMDemodulator.java index dd44a5a1..d3b40479 100644 --- a/src/dsp/psk/LSMDemodulator.java +++ b/src/dsp/psk/LSMDemodulator.java @@ -46,7 +46,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.Complex; import sample.complex.ComplexSampleListener; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeListener; import buffer.FloatAveragingBuffer; import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; @@ -79,7 +79,7 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener private CostasLoop mCostasLoop = new CostasLoop(); - private FrequencyChangeListener mFrequencyChangeListener; + private IFrequencyChangeListener mFrequencyChangeListener; private EyeDiagramDataTap mEyeDiagramDataTap; @@ -153,12 +153,12 @@ public class LSMDemodulator implements Instrumentable, ComplexSampleListener } } - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeListener listener ) { mFrequencyChangeListener = listener; } - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeListener listener ) { mFrequencyChangeListener = null; } diff --git a/src/dsp/psk/LSMDemodulator2.java b/src/dsp/psk/LSMDemodulator2.java index 1df263f1..819af376 100644 --- a/src/dsp/psk/LSMDemodulator2.java +++ b/src/dsp/psk/LSMDemodulator2.java @@ -48,7 +48,7 @@ import sample.Listener; import sample.Provider; import sample.complex.Complex; import sample.complex.ComplexSampleListener; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeListener; import buffer.FloatAveragingBuffer; import dsp.filter.interpolator.RealInterpolator; import dsp.symbol.Dibit; @@ -84,7 +84,7 @@ public class LSMDemodulator2 implements Instrumentable, ComplexSampleListener, private CostasLoop mCostasLoop = new CostasLoop(); - private FrequencyChangeListener mFrequencyChangeListener; + private IFrequencyChangeListener mFrequencyChangeListener; private EyeDiagramDataTap mEyeDiagramDataTap; @@ -143,12 +143,12 @@ public class LSMDemodulator2 implements Instrumentable, ComplexSampleListener, } } - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeListener listener ) { mFrequencyChangeListener = listener; } - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeListener listener ) { mFrequencyChangeListener = null; } diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 79345b07..6de05d02 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -43,7 +43,9 @@ import javax.swing.JOptionPane; import javax.swing.JSeparator; import javax.swing.KeyStroke; +import map.IconManager; import map.MapService; +import module.log.EventLogManager; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -51,7 +53,9 @@ import org.slf4j.LoggerFactory; import playlist.PlaylistManager; import properties.SystemProperties; +import record.RecorderManager; import settings.SettingsManager; +import source.SourceManager; import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import spectrum.SpectralDisplayPanel; @@ -63,7 +67,6 @@ import com.jidesoft.swing.JideSplitPane; import controller.ConfigurationControllerModel; import controller.ControllerPanel; -import controller.ResourceManager; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import controller.channel.ChannelEventListener; @@ -76,6 +79,7 @@ public class SDRTrunk private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); private ControllerPanel mControllerPanel; + private SettingsManager mSettingsManager; private SpectralDisplayPanel mSpectralPanel; private JFrame mMainGui = new JFrame(); @@ -107,49 +111,54 @@ public class SDRTrunk //Log current properties setting SystemProperties.getInstance().logCurrentSettings(); + + mSettingsManager = new SettingsManager(); + + ThreadPoolManager threadPoolManager = new ThreadPoolManager(); + + SourceManager sourceManager = new SourceManager( mSettingsManager, threadPoolManager ); ChannelModel channelModel = new ChannelModel(); + PlaylistManager playlistManager = new PlaylistManager( channelModel ); + + RecorderManager recorderManager = new RecorderManager( threadPoolManager ); + + EventLogManager eventLogManager = new EventLogManager(); + ChannelProcessingManager channelProcessingManager = - new ChannelProcessingManager( channelModel ); + new ChannelProcessingManager( channelModel, eventLogManager, + playlistManager, recorderManager, sourceManager, threadPoolManager ); channelModel.addListener( channelProcessingManager ); - ThreadPoolManager threadPoolManager = new ThreadPoolManager(); + channelProcessingManager.addAudioPacketListener( recorderManager ); AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); - channelProcessingManager.addListener( aliasActionManager ); + channelProcessingManager.addMessageListener( aliasActionManager ); + + AudioManager audioManager = new AudioManager( threadPoolManager ); + channelProcessingManager.addAudioPacketListener( audioManager ); + + MapService mapService = new MapService( mSettingsManager ); + channelProcessingManager.addMessageListener( mapService ); ChannelSelectionManager channelSelectionManager = new ChannelSelectionManager( channelModel ); channelModel.addListener( channelSelectionManager ); - - /** - * Construct the resource manager now, so that it can use the system - * properties that were just loaded - */ - //TODO: get rid of resource manager ... below is start of breaking out - //the encapsulated pieces from the resource manager - PlaylistManager playlistManager = new PlaylistManager( channelModel ); - ResourceManager resource = - new ResourceManager( playlistManager, threadPoolManager, channelModel ); - - AudioManager audio = resource.getAudioManager(); - ConfigurationControllerModel configurationControllerModel = resource.getController(); - MapService map = resource.getMapService(); - SettingsManager settings = resource.getSettingsManager(); + //Initialize the playlist manager to load the saved playlist + playlistManager.init(); - mControllerPanel = - new ControllerPanel( audio, - configurationControllerModel, - channelModel, - channelProcessingManager, - map, - playlistManager, - settings ); + ConfigurationControllerModel configurationControllerModel = + new ConfigurationControllerModel( channelModel, channelProcessingManager, + playlistManager, mSettingsManager, sourceManager ); - mSpectralPanel = new SpectralDisplayPanel( channelModel, - configurationControllerModel, playlistManager, settings ); + mControllerPanel = new ControllerPanel( audioManager, configurationControllerModel, + channelModel, channelProcessingManager, mapService, playlistManager, + mSettingsManager, sourceManager ); + + mSpectralPanel = new SpectralDisplayPanel( configurationControllerModel, + channelModel, channelProcessingManager, playlistManager, mSettingsManager ); mTitle = getTitle(); @@ -162,7 +171,7 @@ public class SDRTrunk { Runnable cml = new ChannelMemoryLogger(); channelModel.addListener( (ChannelEventListener)cml ); - resource.getThreadPoolManager().scheduleFixedRate( + threadPoolManager.scheduleFixedRate( ThreadType.DECODER, cml, 5, TimeUnit.SECONDS ); } @@ -277,7 +286,16 @@ public class SDRTrunk @Override public void actionPerformed( ActionEvent arg0 ) { - mControllerPanel.getController().showIconManager(); + final IconManager iconManager = new IconManager( mSettingsManager, mMainGui ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); } } ); fileMenu.add( settingsMenu ); diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index a413e11f..95b88249 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -33,6 +33,7 @@ import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; @@ -44,17 +45,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; -public class JFrequencyControl extends JPanel implements FrequencyChangeListener +public class JFrequencyControl extends JPanel implements IFrequencyChangeProcessor { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( JFrequencyControl.class ); - private ArrayList mListeners = - new ArrayList(); + private List mProcessors = new ArrayList<>(); private Color mHighlightColor = Color.YELLOW; @@ -136,7 +137,7 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { setFrequency( event.getValue().longValue(), false ); } @@ -177,10 +178,10 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener { updateFrequency(); - Iterator it = mListeners.iterator(); + Iterator it = mProcessors.iterator(); FrequencyChangeEvent event = - new FrequencyChangeEvent( Event.FREQUENCY_CHANGE_NOTIFICATION, mFrequency ); + new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, mFrequency ); while( it.hasNext() ) { @@ -188,14 +189,14 @@ public class JFrequencyControl extends JPanel implements FrequencyChangeListener } } - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeProcessor processor ) { - mListeners.add( listener ); + mProcessors.add( processor ); } - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeProcessor processor ) { - mListeners.remove( listener ); + mProcessors.remove( processor ); } public class Digit extends JTextField diff --git a/src/instrument/InstrumentableProcessingChain.java b/src/instrument/InstrumentableProcessingChain.java index 3efef0ae..94181b18 100644 --- a/src/instrument/InstrumentableProcessingChain.java +++ b/src/instrument/InstrumentableProcessingChain.java @@ -9,11 +9,13 @@ import java.util.List; import module.Module; import module.ProcessingChain; import source.Source; +import controller.channel.Channel.ChannelType; public class InstrumentableProcessingChain extends ProcessingChain implements Instrumentable { public InstrumentableProcessingChain() { + super( ChannelType.STANDARD ); } public void setSource( Source source ) throws IllegalStateException diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index b2b6a32d..998191b1 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -13,8 +13,6 @@ import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; import source.IControllableFileSource; -import controller.ResourceManager; -import controller.ThreadPoolManager; import controller.channel.ChannelModel; public class DecoderSelectionFrame extends JInternalFrame @@ -22,7 +20,7 @@ public class DecoderSelectionFrame extends JInternalFrame private static final long serialVersionUID = 1L; private DecodeComponentEditor mDecodeEditor = new DecodeComponentEditor( null ); - private ResourceManager mResourceManager; + private PlaylistManager mPlaylistManager; private IControllableFileSource mSource; private JDesktopPane mDesktop; @@ -31,9 +29,7 @@ public class DecoderSelectionFrame extends JInternalFrame IControllableFileSource source ) { ChannelModel channelModel = new ChannelModel(); - - mResourceManager = new ResourceManager( new PlaylistManager( channelModel ), - new ThreadPoolManager(), channelModel ); + mPlaylistManager = new PlaylistManager( channelModel ); mDesktop = desktop; mSource = source; @@ -78,12 +74,7 @@ public class DecoderSelectionFrame extends JInternalFrame if( config != null ) { DecoderViewFrame decoderFrame = new DecoderViewFrame( - mResourceManager, - config, - null, - null, - null, - mSource ); + mPlaylistManager, null, mSource ); decoderFrame.setVisible( true ); diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 9cdfe1c5..a2afb439 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -57,22 +57,20 @@ import javax.swing.JSeparator; import message.Message; import module.Module; import module.decode.DecoderFactory; -import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import record.config.RecordConfiguration; +import playlist.PlaylistManager; import sample.Listener; import source.IControllableFileSource; import source.Source; -import alias.AliasList; -import controller.ResourceManager; -import controller.channel.Channel.ChannelType; -import controller.site.Site; -import controller.system.System; +import controller.ThreadPoolManager; +import controller.channel.Channel; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; public class DecoderViewFrame extends JInternalFrame implements Listener @@ -88,29 +86,23 @@ public class DecoderViewFrame extends JInternalFrame private HashMap mPanelMap = new HashMap(); - public DecoderViewFrame( ResourceManager resourceManager, - DecodeConfiguration decodeConfig, - RecordConfiguration recordConfig, - AuxDecodeConfiguration auxConfig, - AliasList aliasList, + public DecoderViewFrame( PlaylistManager playlistManager, + Channel channel, IControllableFileSource source ) { mSource = source; - mDecodeConfig = decodeConfig; + mDecodeConfig = channel.getDecodeConfiguration(); mProcessingChain = new InstrumentableProcessingChain(); + + ChannelModel channelModel = new ChannelModel(); + ChannelProcessingManager channelProcessingManager = + new ChannelProcessingManager( channelModel, null, + playlistManager, null, null, new ThreadPoolManager() ); - List modules = DecoderFactory - .getModules( ChannelType.STANDARD, - resourceManager, - decodeConfig, - recordConfig, - auxConfig, - aliasList, - "Instrumented", - "Instrumented", - "Channel Name" ); + List modules = DecoderFactory.getModules( channelModel, + channelProcessingManager, playlistManager, channel ); mProcessingChain.addModules( modules ); diff --git a/src/map/IconManager.java b/src/map/IconManager.java index 220ca034..9cc07f3b 100644 --- a/src/map/IconManager.java +++ b/src/map/IconManager.java @@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory; import settings.Setting; import settings.SettingChangeListener; -import controller.ResourceManager; +import settings.SettingsManager; public class IconManager extends JFrame implements ActionListener, SettingChangeListener @@ -62,8 +62,6 @@ public class IconManager extends JFrame private final static Logger mLog = LoggerFactory.getLogger( IconManager.class ); - private ResourceManager mResourceManager; - private static final String sMSG_NEW = "Type a name for the new icon, " + "select the file, and click Save,\nor Cancel to reset"; private static final String sMSG_EDIT = "Change the name for the selected \n" @@ -81,9 +79,11 @@ public class IconManager extends JFrame private JFileChooser mFileChooser; private Mode mMode = Mode.NORMAL; - public IconManager( ResourceManager resourceManager, Component displayOver ) + private SettingsManager mSettingsManager; + + public IconManager( SettingsManager settingsManager, Component displayOver ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; init( displayOver ); } @@ -128,7 +128,7 @@ public class IconManager extends JFrame /** * MapIcon list */ - MapIcon[] icons = mResourceManager.getSettingsManager().getMapIcons(); + MapIcon[] icons = mSettingsManager.getMapIcons(); Arrays.sort( icons ); mIconList = new JList( icons ); @@ -176,8 +176,7 @@ public class IconManager extends JFrame @Override public void actionPerformed( ActionEvent e ) { - mResourceManager.getSettingsManager() - .setDefaultIcon( icon ); + mSettingsManager.setDefaultIcon( icon ); refreshList(); } @@ -193,8 +192,7 @@ public class IconManager extends JFrame @Override public void actionPerformed( ActionEvent arg0 ) { - mResourceManager.getSettingsManager() - .deleteMapIcon( icon ); + mSettingsManager.deleteMapIcon( icon ); } } ); @@ -350,7 +348,7 @@ public class IconManager extends JFrame private void refreshList() { - MapIcon[] icons = mResourceManager.getSettingsManager().getMapIcons(); + MapIcon[] icons = mSettingsManager.getMapIcons(); Arrays.sort( icons ); @@ -371,8 +369,7 @@ public class IconManager extends JFrame { if( path != null && !path.isEmpty() ) { - mResourceManager.getSettingsManager() - .addMapIcon( new MapIcon( name, path, true ) ); + mSettingsManager.addMapIcon( new MapIcon( name, path, true ) ); } else { @@ -400,8 +397,7 @@ public class IconManager extends JFrame { selected.setName( name ); selected.setPath( path ); - mResourceManager.getSettingsManager() - .updatMapIcon( selected, name, path ); + mSettingsManager.updatMapIcon( selected, name, path ); } else { diff --git a/src/map/MapPanel.java b/src/map/MapPanel.java index e9b66045..4a61b33f 100644 --- a/src/map/MapPanel.java +++ b/src/map/MapPanel.java @@ -46,17 +46,12 @@ public class MapPanel extends JPanel implements PlottableUpdateListener private JXMapViewer mMapViewer = new JXMapViewer(); private PlottableEntityPainter mMapPainter; - public MapPanel( MapService mapService, - SettingsManager settingsManager, - ChannelProcessingManager channelProcessingManager ) + public MapPanel( MapService mapService, SettingsManager settingsManager ) { mSettingsManager = settingsManager; mMapService = mapService; mMapPainter = new PlottableEntityPainter( mSettingsManager ); - /* Add Map Service as message listener to receive all messages */ - channelProcessingManager.addListener( (Listener)mMapService ); - init(); } diff --git a/src/map/MapService.java b/src/map/MapService.java index 0b6c9f87..0c251277 100644 --- a/src/map/MapService.java +++ b/src/map/MapService.java @@ -30,7 +30,6 @@ import sample.Listener; import settings.SettingsManager; import alias.Alias; import controller.NamingThreadFactory; -import controller.ResourceManager; public class MapService implements Listener { @@ -49,11 +48,11 @@ public class MapService implements Listener private HashMap mEntities = new HashMap(); - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; - public MapService( ResourceManager resourceManager ) + public MapService( SettingsManager resourceManager ) { - mResourceManager = resourceManager; + mSettingsManager = resourceManager; // scheduler.scheduleAtFixedRate( new CullThread(), 5, 5, TimeUnit.MINUTES ); } @@ -111,8 +110,7 @@ public class MapService implements Listener iconName = SettingsManager.DEFAULT_ICON; } - icon = mResourceManager.getSettingsManager() - .getMapIcon( iconName ); + icon = mSettingsManager.getMapIcon( iconName ); } if( color == null ) diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index c8e3da4f..68c94256 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * Copyright (C) 2015-2016 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 @@ -26,8 +26,11 @@ import message.IMessageListener; import message.IMessageProvider; import message.Message; import module.decode.event.CallEvent; +import module.decode.event.CallEventModel; import module.decode.event.ICallEventListener; import module.decode.event.ICallEventProvider; +import module.decode.event.MessageActivityModel; +import module.decode.state.ChannelState; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; import module.decode.state.DecoderStateEvent.Event; @@ -42,16 +45,19 @@ import sample.Broadcaster; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; -import sample.real.IRealBufferListener; -import sample.real.IRealBufferProvider; +import sample.real.IFilteredRealBufferListener; +import sample.real.IFilteredRealBufferProvider; +import sample.real.IUnFilteredRealBufferListener; +import sample.real.IUnFilteredRealBufferProvider; import sample.real.RealBuffer; import source.ComplexSource; import source.RealSource; import source.Source; import source.SourceException; import source.tuner.TunerChannelSource; -import source.tuner.frequency.FrequencyCorrectionControl; -import source.tuner.frequency.IFrequencyCorrectionController; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.IFrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProvider; import audio.AudioPacket; import audio.IAudioPacketListener; import audio.IAudioPacketProvider; @@ -62,6 +68,7 @@ import audio.squelch.ISquelchStateListener; import audio.squelch.ISquelchStateProvider; import audio.squelch.SquelchState; import controller.ThreadPoolManager; +import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.IChannelEventListener; import controller.channel.IChannelEventProvider; @@ -95,8 +102,10 @@ public class ProcessingChain implements IChannelEventListener private Broadcaster mChannelEventBroadcaster = new Broadcaster<>(); private Broadcaster mComplexBufferBroadcaster = new Broadcaster<>(); private Broadcaster mDecoderStateEventBroadcaster = new Broadcaster<>(); + private Broadcaster mFrequencyChangeEventBroadcaster = new Broadcaster<>(); private Broadcaster mMessageBroadcaster = new Broadcaster<>(); - private Broadcaster mRealBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mFilteredRealBufferBroadcaster = new Broadcaster<>(); + private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); // private ThreadPoolManager mThreadPoolManager; @@ -104,12 +113,50 @@ public class ProcessingChain implements IChannelEventListener protected Source mSource; private List mModules = new ArrayList<>(); - private IFrequencyCorrectionController mFrequencyCorrectionController; + private CallEventModel mCallEventModel; + private ChannelState mChannelState; + private MessageActivityModel mMessageActivityModel; - public ProcessingChain() + public ProcessingChain( ChannelType channelType ) { + this( new ThreadPoolManager(), channelType ); } + + public ProcessingChain( ThreadPoolManager threadManager, + ChannelType channelType ) + { + mThreadPoolManager = threadManager; + + mChannelState = new ChannelState( mThreadPoolManager, channelType ); + addModule( mChannelState ); + + mCallEventModel = new CallEventModel(); + addCallEventListener( mCallEventModel ); + } + + public MessageActivityModel getMessageActivityModel() + { + return mMessageActivityModel; + } + + public CallEventModel getCallEventModel() + { + return mCallEventModel; + } + + public ChannelState getChannelState() + { + return mChannelState; + } + + public void setMessageActivityModel( MessageActivityModel model ) + { + mMessageActivityModel = model; + + addMessageListener( mMessageActivityModel ); + } + public void dispose() { stop(); @@ -121,14 +168,16 @@ public class ProcessingChain implements IChannelEventListener mModules.clear(); - mFrequencyCorrectionController = null; + + mThreadPoolManager = null; + mBufferProcessingTask = null; mAudioPacketBroadcaster.dispose(); mCallEventBroadcaster.dispose(); mChannelEventBroadcaster.dispose(); mComplexBufferBroadcaster.dispose(); mMessageBroadcaster.dispose(); - mRealBufferBroadcaster.dispose(); + mFilteredRealBufferBroadcaster.dispose(); mSquelchStateBroadcaster.dispose(); } @@ -147,7 +196,7 @@ public class ProcessingChain implements IChannelEventListener * Indicates if this processing chain is currently receiving samples from * a source and sending those samples to the decoders. */ - public boolean processing() + public boolean isProcessing() { return mRunning.get(); } @@ -160,28 +209,6 @@ public class ProcessingChain implements IChannelEventListener return mSource != null; } - /** - * Indicates if one of the modules provides frequency error control. - */ - public boolean hasFrequencyCorrectionControl() - { - return mFrequencyCorrectionController != null && - mFrequencyCorrectionController.hasFrequencyCorrectionControl(); - } - - /** - * Frequency correction controller. Can be null. - */ - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - if( mFrequencyCorrectionController != null ) - { - return mFrequencyCorrectionController.getFrequencyCorrectionControl(); - } - - return null; - } - /** * Applies a sample source to this processing chain. Processing won't * start until the start() method is invoked. @@ -194,7 +221,7 @@ public class ProcessingChain implements IChannelEventListener */ public void setSource( Source source ) throws IllegalStateException { - if( processing() ) + if( isProcessing() ) { throw new IllegalStateException( "Processing chain is currently " + "processing. Invoke stop() on the processing chain before " @@ -202,23 +229,6 @@ public class ProcessingChain implements IChannelEventListener } mSource = source; - - /* Establish two-way communication between the tuner channel source and - * the decoder for fine tuning frequency error correction feedback from - * the decoder. The tuner channel source will also notify the frequency - * controller during frequency changes to allow the controller to reset - * frequency error tracking */ - if( mSource != null && - mSource instanceof TunerChannelSource && - hasFrequencyCorrectionControl() ) - { - TunerChannelSource channel = (TunerChannelSource)mSource; - - FrequencyCorrectionControl control = getFrequencyCorrectionControl(); - - channel.setFrequencyChangeListener( control ); - control.setFrequencyChangeListener( channel ); - } } /** @@ -247,24 +257,45 @@ public class ProcessingChain implements IChannelEventListener return decoderStates; } + /** + * Adds the list of modules to this processing chain + */ + public void addModules( List modules ) + { + for( Module module: modules ) + { + addModule( module ); + } + } + /** * Adds a module to the processing chain. Each module is tested for the * interfaces that it supports and is registered or receives a listener * to consume or produce the supported interface data type. All elements - * that are produced by any component are automatically routed to all other - * components that support the corresponding listener interface. + * and events that are produced by any module are automatically routed to + * all other components that support the corresponding listener interface. * * At least one module should consume complex samples and either produce * decoded messages and/or audio, or produce decoded real sample buffers * for all other modules to consume. * - * @param module - processing module, demodulator or decoder + * @param module - processing module, demodulator, decoder, source, state + * machine, etc. */ public void addModule( Module module ) { mModules.add( module ); + + registerListeners( module ); + registerProviders( module ); + } - /* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Listeners <<<<<<<<<<<<<<<<<<<<<<<<<<< */ + /** + * Registers the module as a listener to each of the broadcasters that + * provide the data interface(s) supported by the module. + */ + private void registerListeners( Module module ) + { if( module instanceof IAudioPacketListener ) { mAudioPacketBroadcaster.addListener( @@ -295,6 +326,12 @@ public class ProcessingChain implements IChannelEventListener ((IDecoderStateEventListener)module).getDecoderStateListener() ); } + if( module instanceof IFrequencyChangeListener ) + { + mFrequencyChangeEventBroadcaster.addListener( + ((IFrequencyChangeListener)module).getFrequencyChangeListener()); + } + if( module instanceof IMessageListener ) { mMessageBroadcaster.addListener( @@ -307,10 +344,10 @@ public class ProcessingChain implements IChannelEventListener ((IMetadataListener)module).getMetadataListener() ); } - if( module instanceof IRealBufferListener ) + if( module instanceof IFilteredRealBufferListener ) { - mRealBufferBroadcaster.addListener( - ((IRealBufferListener)module).getRealBufferListener() ); + mFilteredRealBufferBroadcaster.addListener( + ((IFilteredRealBufferListener)module).getFilteredRealBufferListener() ); } if( module instanceof ISquelchStateListener ) @@ -318,8 +355,20 @@ public class ProcessingChain implements IChannelEventListener mSquelchStateBroadcaster.addListener( ((ISquelchStateListener)module).getSquelchStateListener() ); } - - /* >>>>>>>>>>>>>>>>>>> Providers <<<<<<<<<<<<<<<<<<<<<<<<< */ + + if( module instanceof IUnFilteredRealBufferListener ) + { + mFilteredRealBufferBroadcaster.addListener( + ((IUnFilteredRealBufferListener)module).getUnFilteredRealBufferListener() ); + } + } + + /** + * Registers the broadcaster(s) as listeners to the module for each + * provider interface that is supported by the module. + */ + private void registerProviders( Module module ) + { if( module instanceof IAudioPacketProvider ) { ((IAudioPacketProvider)module).setAudioPacketListener( @@ -342,12 +391,13 @@ public class ProcessingChain implements IChannelEventListener ((IDecoderStateEventProvider)module).setDecoderStateListener( mDecoderStateEventBroadcaster ); } - - if( module instanceof IFrequencyCorrectionController ) - { - mFrequencyCorrectionController = (IFrequencyCorrectionController)module; - } + if( module instanceof IFrequencyChangeProvider ) + { + ((IFrequencyChangeProvider)module).setFrequencyChangeListener( + mFrequencyChangeEventBroadcaster ); + } + if( module instanceof IMessageProvider ) { ((IMessageProvider)module).addMessageListener( mMessageBroadcaster ); @@ -359,9 +409,10 @@ public class ProcessingChain implements IChannelEventListener mMetadataBroadcaster ); } - if( module instanceof IRealBufferProvider ) + if( module instanceof IFilteredRealBufferProvider ) { - ((IRealBufferProvider)module).setRealBufferListener( mRealBufferBroadcaster ); + ((IFilteredRealBufferProvider)module) + .setFilteredRealBufferListener( mFilteredRealBufferBroadcaster ); } if( module instanceof ISquelchStateProvider ) @@ -369,19 +420,13 @@ public class ProcessingChain implements IChannelEventListener ((ISquelchStateProvider)module).setSquelchStateListener( mSquelchStateBroadcaster ); } - } - - /** - * Adds the list of modules to this processing chain - */ - public void addModules( List modules ) - { - for( Module module: modules ) + + if( module instanceof IUnFilteredRealBufferProvider ) { - addModule( module ); + ((IUnFilteredRealBufferProvider)module) + .setUnFilteredRealBufferListener( mUnFilteredRealBufferBroadcaster ); } } - /** * Starts processing if the chain has a valid source. Invocations on an @@ -421,7 +466,7 @@ public class ProcessingChain implements IChannelEventListener ((ComplexSource)mSource).setListener( mComplexBufferBroadcaster ); break; case REAL: - ((RealSource)mSource).setListener( mRealBufferBroadcaster ); + ((RealSource)mSource).setListener( mFilteredRealBufferBroadcaster ); break; default: throw new IllegalArgumentException( "Unrecognized source " @@ -448,7 +493,7 @@ public class ProcessingChain implements IChannelEventListener } else { - mLog.debug( "Source is null on start()" ); + mLog.error( "Source is null on start()" ); } } } @@ -469,7 +514,7 @@ public class ProcessingChain implements IChannelEventListener ((ComplexSource)mSource).removeListener( mComplexBufferBroadcaster ); break; case REAL: - ((RealSource)mSource).removeListener( mRealBufferBroadcaster ); + ((RealSource)mSource).removeListener( mFilteredRealBufferBroadcaster ); break; default: throw new IllegalArgumentException( "Unrecognized source " @@ -585,12 +630,12 @@ public class ProcessingChain implements IChannelEventListener public void addRealBufferListener( Listener listener ) { - mRealBufferBroadcaster.addListener( listener ); + mFilteredRealBufferBroadcaster.addListener( listener ); } public void removeRealBufferListener( Listener listener ) { - mRealBufferBroadcaster.removeListener( listener ); + mFilteredRealBufferBroadcaster.removeListener( listener ); } @Override diff --git a/src/module/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeComponentEditor.java index dd55ee59..8e3dc8cb 100644 --- a/src/module/decode/AuxDecodeComponentEditor.java +++ b/src/module/decode/AuxDecodeComponentEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -26,7 +26,8 @@ import javax.swing.SwingUtilities; import module.decode.config.AuxDecodeConfiguration; import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; public class AuxDecodeComponentEditor extends AbstractChannelEditor { @@ -35,9 +36,9 @@ public class AuxDecodeComponentEditor extends AbstractChannelEditor private HashMap mControls = new HashMap(); - public AuxDecodeComponentEditor( ChannelNode channelNode ) + public AuxDecodeComponentEditor( Channel channel ) { - super( channelNode ); + super( channel ); List decoders = DecoderType.getAuxDecoders(); @@ -60,7 +61,7 @@ public class AuxDecodeComponentEditor extends AbstractChannelEditor public AuxDecodeConfiguration getConfig() { - return getChannelNode().getChannel().getAuxDecodeConfiguration(); + return getChannel().getAuxDecodeConfiguration(); } @Override @@ -107,4 +108,18 @@ public class AuxDecodeComponentEditor extends AbstractChannelEditor mDecoderType = decoder; } } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/module/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java index bec8de84..bdc48706 100644 --- a/src/module/decode/DecodeComponentEditor.java +++ b/src/module/decode/DecodeComponentEditor.java @@ -1,6 +1,7 @@ /******************************************************************************* + * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,7 +18,6 @@ ******************************************************************************/ package module.decode; - import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -30,11 +30,10 @@ import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; -import controller.Editor; import controller.channel.AbstractChannelEditor; import controller.channel.Channel; -import controller.channel.ChannelNode; -import controller.channel.ChannelValidationException; +import controller.channel.ChannelConfigurationEditor; +import controller.channel.ConfigurationValidationException; public class DecodeComponentEditor extends AbstractChannelEditor { @@ -48,13 +47,12 @@ public class DecodeComponentEditor extends AbstractChannelEditor public DecodeComponentEditor( PlaylistManager playlistManager ) { - this( new ChannelNode( new Channel() ), playlistManager ); + this( playlistManager, new Channel() ); } - public DecodeComponentEditor( ChannelNode channelNode, - PlaylistManager playlistMangager ) + public DecodeComponentEditor( PlaylistManager playlistMangager, Channel channel ) { - super( channelNode ); + super( channel ); mPlaylistManager = playlistMangager; @@ -87,17 +85,13 @@ public class DecodeComponentEditor extends AbstractChannelEditor { DecodeConfiguration config; - if( mChannelNode.getChannel() - .getDecodeConfiguration().getDecoderType() == - selected ) + if( mChannel.getDecodeConfiguration().getDecoderType() == selected ) { - config = mChannelNode.getChannel() - .getDecodeConfiguration(); + config = mChannel.getDecodeConfiguration(); } else { - config = DecodeConfigFactory - .getDecodeConfiguration( selected ); + config = DecodeConfigFactory.getDecodeConfiguration( selected ); } //Remove the existing one @@ -108,7 +102,7 @@ public class DecodeComponentEditor extends AbstractChannelEditor //Change to the new one mEditor = DecoderFactory.getEditorPanel( config, - mChannelNode, mPlaylistManager ); + mChannel, mPlaylistManager ); add( mEditor, "span 2,center" ); @@ -125,8 +119,7 @@ public class DecodeComponentEditor extends AbstractChannelEditor public void reset() { - final DecoderType decoder = mChannelNode.getChannel() - .getDecodeConfiguration().getDecoderType(); + final DecoderType decoder = mChannel.getDecodeConfiguration().getDecoderType(); SwingUtilities.invokeLater(new Runnable() { @@ -155,18 +148,31 @@ public class DecodeComponentEditor extends AbstractChannelEditor } //The component that calls save will invoke the change broadcast - mChannelNode.getChannel() - .setDecodeConfiguration( mEditor.getConfig() ); + mChannel.setDecodeConfiguration( mEditor.getConfig() ); } - /** - * Validates the editor against the current decode editor - */ +// /** +// * Validates the editor against the current decode editor +// */ +// @Override +// public void validate( ChannelConfigurationEditor editor ) throws ConfigurationValidationException +// { +// super.validate( editor ); +// +// mEditor.validate( editor ); +// } + @Override - public void validate( Editor editor ) throws ChannelValidationException + public void setConfiguration( Channel channel ) { - super.validate( editor ); + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub - mEditor.validate( editor ); } } diff --git a/src/module/decode/DecodeEditor.java b/src/module/decode/DecodeEditor.java index dcebf156..cddc6082 100644 --- a/src/module/decode/DecodeEditor.java +++ b/src/module/decode/DecodeEditor.java @@ -31,9 +31,11 @@ import javax.swing.event.ChangeListener; import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; -import controller.Editor; +import controller.channel.Channel; +import controller.channel.ChannelConfigurationEditor; +import controller.channel.ConfigurationValidationException; -public class DecodeEditor extends Editor +public class DecodeEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; @@ -110,7 +112,16 @@ public class DecodeEditor extends Editor } @Override - public void reset() - { - } + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 2acc06b1..109c0017 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -63,10 +63,10 @@ import module.decode.nbfm.NBFMDecoder; import module.decode.nbfm.NBFMDecoderPanel; import module.decode.nbfm.NBFMEditor; import module.decode.p25.DecodeConfigP25Phase1; +import module.decode.p25.P25DecodeEditor; import module.decode.p25.P25Decoder.Modulation; import module.decode.p25.P25DecoderPanel; import module.decode.p25.P25DecoderState; -import module.decode.p25.P25DecodeEditor; import module.decode.p25.P25_C4FMDecoder; import module.decode.p25.P25_LSMDecoder; import module.decode.p25.audio.P25AudioModule; @@ -84,6 +84,7 @@ import module.decode.tait.Tait1200Decoder; import module.decode.tait.Tait1200DecoderPanel; import module.decode.tait.Tait1200DecoderState; import module.demodulate.am.AMDemodulatorModule; +import module.demodulate.audio.DemodulatedAudioFilterModule; import module.demodulate.fm.FMDemodulatorModule; import org.slf4j.Logger; @@ -97,12 +98,11 @@ import settings.SettingsManager; import util.TimeStamp; import alias.AliasList; import audio.AudioModule; -import controller.ResourceManager; +import controller.channel.Channel; import controller.channel.Channel.ChannelType; -import controller.channel.ChannelNode; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; import controller.channel.map.ChannelMap; -import controller.site.Site; -import controller.system.System; import filter.AllPassFilter; import filter.FilterSet; import filter.IFilter; @@ -120,72 +120,46 @@ public class DecoderFactory * Returns a list of one primary decoder and any auxiliary decoders, as * specified in the configurations. * - * @param channelType - standard or traffic channel type - * @param resourceManager - * @param decodeConfig - primary decoder configuration - * @param recordConfig - recording configuration used by traffic channel manager - * @param auxConfig - auxiliary decoder(s) configuration - * @param optional alias list - (null is ok) - * * @return list of configured decoders */ - public static List getModules( ChannelType channelType, - ResourceManager resourceManager, - DecodeConfiguration decodeConfig, - RecordConfiguration recordConfig, - AuxDecodeConfiguration auxConfig, - AliasList aliasList, - String system, - String site, - String channelName ) + public static List getModules( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + Channel channel ) { - List modules = getPrimaryModules( channelType, decodeConfig, - recordConfig, aliasList, system, site, channelName, resourceManager ); - modules.addAll( getAuxiliaryDecoders( auxConfig, aliasList ) ); + /* Get the optional alias list for the decode modules to use */ + AliasList aliasList = playlistManager.getPlayist() + .getAliasDirectory().getAliasList( channel.getAliasListName() ); + + List modules = getPrimaryModules( channelModel, channelProcessingManager, + playlistManager, aliasList, channel ); + + modules.addAll( getAuxiliaryDecoders( channel.getAuxDecodeConfiguration(), aliasList ) ); return modules; } - public static FMDemodulatorModule getFMDemodulator( - DecodeConfiguration config, int pass, int stop, boolean removeDC ) - { - if( config.isAFCEnabled() ) - { - return new FMDemodulatorModule( pass, stop, - config.getAFCMaximumCorrection(), removeDC ); - } - else - { - return new FMDemodulatorModule( pass, stop, - NO_FREQUENCY_CORRECTION, removeDC ); - } - } - /** * Constructs a primary decoder as specified in the decode configuration - * - * @param channelType - traffic or standard decode channel - * @param decodeConfig - primary decoder configuration - * @param recordConfig - recording options - * @param aliasList - optional alias list - * @param resourceManager - shared resource manager - * @return configured decoder or null */ - public static List getPrimaryModules( ChannelType channelType, - DecodeConfiguration decodeConfig, - RecordConfiguration recordConfig, + public static List getPrimaryModules( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, AliasList aliasList, - String system, - String site, - String channelName, - ResourceManager resourceManager ) + Channel channel ) { List modules = new ArrayList(); + ChannelType channelType = channel.getChannelType(); + /* Baseband low-pass filter pass and stop frequencies */ - int pass = decodeConfig.getDecoderType().getChannelBandwidth() / 2; - int stop = pass + 1250; + DecodeConfiguration decodeConfig = channel.getDecodeConfiguration(); + + int iqPass = decodeConfig.getDecoderType().getChannelBandwidth() / 2; + int iqStop = iqPass + 1250; + + RecordConfiguration recordConfig = channel.getRecordConfiguration(); boolean recordAudio = recordConfig == null ? false : recordConfig.getRecorders().contains( RecorderType.AUDIO ); @@ -193,41 +167,44 @@ public class DecoderFactory switch( decodeConfig.getDecoderType() ) { case AM: - modules.add( new AMDemodulatorModule() ); modules.add( new AMDecoder( decodeConfig ) ); - modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.AM, channelName ) ); - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); + modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.AM, channel.getName() ) ); + modules.add( new AMDemodulatorModule() ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case NBFM: - modules.add( new AudioModule( recordAudio, NO_REMOVE_DC ) ); modules.add( new NBFMDecoder( decodeConfig ) ); - modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.NBFM, channelName ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, REMOVE_DC ) ); + modules.add( new AlwaysUnsquelchedDecoderState( DecoderType.NBFM, channel.getName() ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case LTR_STANDARD: - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); MessageDirection direction = ((DecodeConfigLTRStandard)decodeConfig).getMessageDirection(); modules.add( new LTRStandardDecoder( aliasList, direction ) ); modules.add( new LTRStandardDecoderState( aliasList ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, NO_REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case LTR_NET: - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); modules.add( new LTRNetDecoder( (DecodeConfigLTRNet)decodeConfig, aliasList ) ); modules.add( new LTRNetDecoderState( aliasList ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, NO_REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case MPT1327: DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)decodeConfig; - modules.add( new AudioModule( recordAudio, NO_REMOVE_DC ) ); - - ChannelMap channelMap = resourceManager.getPlaylistManager().getPlayist() + ChannelMap channelMap = playlistManager.getPlayist() .getChannelMapList().getChannelMap( mptConfig.getChannelMapName() ); Sync sync = mptConfig.getSync(); modules.add( new MPT1327Decoder( aliasList, sync ) ); + modules.add( new MPT1327DecoderState( aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000 ) ); @@ -235,30 +212,34 @@ public class DecoderFactory { long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds - modules.add( new TrafficChannelManager( resourceManager, - decodeConfig, recordConfig, system, site, - ( aliasList != null ? aliasList.getName() : null ), timeout, + modules.add( new TrafficChannelManager( channelModel, channelProcessingManager, + decodeConfig, recordConfig, channel.getSystem(), + channel.getSite(), ( aliasList != null ? aliasList.getName() : null ), timeout, mptConfig.getTrafficChannelPoolSize() ) ); } - modules.add( getFMDemodulator( decodeConfig, pass, stop, REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case PASSPORT: - modules.add( new AudioModule( recordAudio, REMOVE_DC ) ); modules.add( new PassportDecoder( decodeConfig, aliasList ) ); modules.add( new PassportDecoderState( aliasList ) ); - modules.add( getFMDemodulator( decodeConfig, pass, stop, NO_REMOVE_DC ) ); + modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); + modules.add( new DemodulatedAudioFilterModule( 4000, 6000 )); + modules.add( new AudioModule( recordAudio ) ); break; case P25_PHASE1: DecodeConfigP25Phase1 p25Config = (DecodeConfigP25Phase1)decodeConfig; Modulation modulation = p25Config.getModulation(); + switch( modulation ) { case C4FM: - modules.add( getFMDemodulator( decodeConfig, 6750, 7500, NO_REMOVE_DC ) ); - modules.add( new P25_C4FMDecoder( aliasList ) ); + modules.add( new FMDemodulatorModule( 6750, 7500 ) ); + modules.add( new P25_C4FMDecoder( aliasList, decodeConfig.getAFCMaximumCorrection() ) ); modules.add( new P25DecoderState( aliasList, channelType, Modulation.C4FM, p25Config.getIgnoreDataCalls() ) ); break; @@ -277,8 +258,9 @@ public class DecoderFactory //Set call timeout to 3 seconds for P25 traffic channels long timeout = 3000; - modules.add( new TrafficChannelManager( resourceManager, - decodeConfig, recordConfig, system, site, + modules.add( new TrafficChannelManager( channelModel, + channelProcessingManager, decodeConfig, + recordConfig, channel.getSystem(), channel.getSite(), ( aliasList != null ? aliasList.getName() : null ), timeout, p25Config.getTrafficChannelPoolSize() ) ); } @@ -449,7 +431,7 @@ public class DecoderFactory } public static DecodeEditor getEditorPanel( DecodeConfiguration config, - ChannelNode channelNode, PlaylistManager playlistManager ) + Channel channel, PlaylistManager playlistManager ) { DecodeEditor configuredPanel; @@ -466,7 +448,7 @@ public class DecoderFactory break; case MPT1327: configuredPanel = new MPT1327ConfigEditor( config, - channelNode, playlistManager ); + channel, playlistManager ); break; case PASSPORT: configuredPanel = new PassportEditor( config ); diff --git a/src/module/decode/event/CallEventPanel.java b/src/module/decode/event/CallEventPanel.java index c9ac7ac0..48d8ce34 100644 --- a/src/module/decode/event/CallEventPanel.java +++ b/src/module/decode/event/CallEventPanel.java @@ -23,12 +23,14 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; +import module.ProcessingChain; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import controller.channel.ChannelProcessingManager; public class CallEventPanel extends JPanel implements ChannelEventListener { @@ -37,9 +39,13 @@ public class CallEventPanel extends JPanel implements ChannelEventListener private JScrollPane mEmptyScroller; private Channel mDisplayedChannel; private CallEventAliasCellRenderer mRenderer; + private ChannelProcessingManager mChannelProcessingManager; - public CallEventPanel( SettingsManager settingsManager ) + public CallEventPanel( SettingsManager settingsManager, + ChannelProcessingManager channelProcessingManager ) { + mChannelProcessingManager = channelProcessingManager; + setLayout( new MigLayout("insets 0 0 0 0 ", "[grow,fill]", "[grow,fill]") ); JTable table = new JTable( new CallEventModel() ); @@ -69,34 +75,43 @@ public class CallEventPanel extends JPanel implements ChannelEventListener ( mDisplayedChannel != null && mDisplayedChannel != event.getChannel() ) ) { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - removeAll(); - - JTable table = new JTable( - event.getChannel().getCallEventModel() ); - - table.setAutoCreateRowSorter( true ); - table.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); - - table.getColumnModel().getColumn( CallEventModel.FROM_ALIAS ) - .setCellRenderer( mRenderer ); - - table.getColumnModel().getColumn( CallEventModel.TO_ALIAS ) - .setCellRenderer( mRenderer ); - - add( new JScrollPane( table ) ); - - mDisplayedChannel = event.getChannel(); - - revalidate(); - repaint(); - } - } ); + ProcessingChain chain = mChannelProcessingManager + .getProcessingChain( event.getChannel() ); + if( chain != null ) + { + final CallEventModel model = chain.getCallEventModel(); + + if( model != null ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + removeAll(); + + JTable table = new JTable( model ); + + table.setAutoCreateRowSorter( true ); + table.setAutoResizeMode( JTable.AUTO_RESIZE_LAST_COLUMN ); + + table.getColumnModel().getColumn( CallEventModel.FROM_ALIAS ) + .setCellRenderer( mRenderer ); + + table.getColumnModel().getColumn( CallEventModel.TO_ALIAS ) + .setCellRenderer( mRenderer ); + + add( new JScrollPane( table ) ); + + mDisplayedChannel = event.getChannel(); + + revalidate(); + repaint(); + } + } ); + } + } } } else if( event.getEvent() == Event.NOTIFICATION_PROCESSING_STOP || diff --git a/src/module/decode/event/MessageActivityPanel.java b/src/module/decode/event/MessageActivityPanel.java index 9f84d619..6cc4ac71 100644 --- a/src/module/decode/event/MessageActivityPanel.java +++ b/src/module/decode/event/MessageActivityPanel.java @@ -36,6 +36,7 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import message.Message; +import module.ProcessingChain; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -48,6 +49,7 @@ import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; +import controller.channel.ChannelProcessingManager; import filter.FilterEditorPanel; import filter.FilterSet; @@ -63,16 +65,21 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener private JTable mMessageTable; private Channel mDisplayedChannel; + private MessageActivityModel mDisplayedModel = EMPTY_MODEL; private MessageManagementPanel mManagementPanel = new MessageManagementPanel(); + + private ChannelProcessingManager mChannelProcessingManager; - public MessageActivityPanel() + public MessageActivityPanel( ChannelProcessingManager channelProcessingManager ) { + mChannelProcessingManager = channelProcessingManager; + setLayout( new MigLayout("insets 0 0 0 0", "[][grow,fill]", "[]0[grow,fill]") ); add( mManagementPanel, "span,growx" ); - mMessageTable = new JTable( EMPTY_MODEL ); + mMessageTable = new JTable( mDisplayedModel ); add( new JScrollPane( mMessageTable ), "span,grow" ); } @@ -91,7 +98,19 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener { mDisplayedChannel = event.getChannel(); - mMessageTable.setModel( event.getChannel().getMessageActivityModel() ); + ProcessingChain chain = mChannelProcessingManager + .getProcessingChain( mDisplayedChannel ); + + if( chain != null ) + { + mDisplayedModel = chain.getMessageActivityModel(); + } + else + { + mDisplayedModel = EMPTY_MODEL; + } + + mMessageTable.setModel( mDisplayedModel ); if( mDisplayedChannel != null ) { @@ -195,10 +214,9 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener public void mouseReleased( MouseEvent arg0 ) {} } ); - if( mDisplayedChannel != null ) + if( mDisplayedModel != null ) { - slider.setValue( mDisplayedChannel.getMessageActivityModel() - .getMaxMessageCount() ); + slider.setValue( mDisplayedModel.getMaxMessageCount() ); } else { @@ -210,10 +228,9 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener @Override public void stateChanged( ChangeEvent arg0 ) { - if( mDisplayedChannel != null ) + if( mDisplayedModel != null ) { - mDisplayedChannel.getMessageActivityModel() - .setMaxMessageCount( slider.getValue() ); + mDisplayedModel.setMaxMessageCount( slider.getValue() ); } } }); @@ -228,9 +245,9 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener @Override public void actionPerformed( ActionEvent e ) { - if( mDisplayedChannel != null ) + if( mDisplayedModel != null ) { - mDisplayedChannel.getMessageActivityModel().clear(); + mDisplayedModel.clear(); } } } ); @@ -258,50 +275,52 @@ public class MessageActivityPanel extends JPanel implements ChannelEventListener } else { - final JFrame editor = new JFrame(); - - editor.setTitle( "Message Filter Editor" ); - editor.setSize( 600, 400 ); - editor.setLocationRelativeTo( MessageFilterButton.this ); - editor.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); - editor.setLayout( new MigLayout( "", "[grow,fill]", - "[grow,fill][][]" ) ); - - @SuppressWarnings( "unchecked" ) - FilterSet filter = (FilterSet) - mDisplayedChannel.getMessageActivityModel() - .getMessageFilter(); - - FilterEditorPanel panel = - new FilterEditorPanel( filter ); - - JScrollPane scroller = new JScrollPane( panel ); - scroller.setViewportView( panel ); - - editor.add( scroller, "wrap" ); - - editor.add( new JLabel( "Right-click to select/deselect " - + "all nodes" ), "wrap" ); - - JButton close = new JButton( "Close" ); - close.addActionListener( new ActionListener() + if( mDisplayedModel != null ) { - @Override - public void actionPerformed( ActionEvent e ) - { - editor.dispose(); - } - } ); - - editor.add( close ); + final JFrame editor = new JFrame(); + + editor.setTitle( "Message Filter Editor" ); + editor.setSize( 600, 400 ); + editor.setLocationRelativeTo( MessageFilterButton.this ); + editor.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + editor.setLayout( new MigLayout( "", "[grow,fill]", + "[grow,fill][][]" ) ); - EventQueue.invokeLater( new Runnable() - { - public void run() - { - editor.setVisible( true ); - } - } ); + @SuppressWarnings( "unchecked" ) + FilterSet filter = (FilterSet) + mDisplayedModel.getMessageFilter(); + + FilterEditorPanel panel = + new FilterEditorPanel( filter ); + + JScrollPane scroller = new JScrollPane( panel ); + scroller.setViewportView( panel ); + + editor.add( scroller, "wrap" ); + + editor.add( new JLabel( "Right-click to select/deselect " + + "all nodes" ), "wrap" ); + + JButton close = new JButton( "Close" ); + close.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + editor.dispose(); + } + } ); + + editor.add( close ); + + EventQueue.invokeLater( new Runnable() + { + public void run() + { + editor.setVisible( true ); + } + } ); + } } } } ); diff --git a/src/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java index 1425983a..f8092472 100644 --- a/src/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -29,7 +29,7 @@ import java.util.List; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -44,7 +44,7 @@ import dsp.fsk.FSK2Decoder.Output; * Fleetsync II Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * floating point samples and output fully framed Fleetsync II messages */ -public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, +public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -169,7 +169,7 @@ public class Fleetsync2Decoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } diff --git a/src/module/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java index 57297d57..1e20eeda 100644 --- a/src/module/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -31,7 +31,7 @@ import module.decode.Decoder; import module.decode.DecoderType; import sample.Broadcaster; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import sample.real.RealSampleListener; import alias.AliasList; @@ -46,7 +46,7 @@ import dsp.fsk.FSK2Decoder.Output; /** * LJ1200 - 1200 baud 2FSK decoder */ -public class LJ1200Decoder extends Decoder implements IRealBufferListener, +public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -187,7 +187,7 @@ public class LJ1200Decoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java index 095dffe4..f5c1d3ca 100644 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -28,14 +28,15 @@ import message.MessageDirection; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IUnFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; import bits.SyncPattern; import dsp.fsk.LTRFSKDecoder; -public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instrumentable +public class LTRNetDecoder extends Decoder + implements IUnFilteredRealBufferListener, Instrumentable { public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; private LTRFSKDecoder mLTRFSKDecoder; @@ -44,6 +45,10 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr private List mAvailableTaps; + /** + * LTR-Net Decoder. Decodes unfiltered (e.g. demodulated but with no DC or + * audio filtering) samples and produces LTR-Net messages. + */ public LTRNetDecoder( DecodeConfigLTRNet config, AliasList aliasList ) { mLTRFSKDecoder = new LTRFSKDecoder(); @@ -79,7 +84,7 @@ public class LTRNetDecoder extends Decoder implements IRealBufferListener, Instr } @Override - public Listener getRealBufferListener() + public Listener getUnFilteredRealBufferListener() { return mLTRFSKDecoder; } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java index a39d659c..228d68f5 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -21,14 +21,14 @@ import message.MessageDirection; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IUnFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; import bits.SyncPattern; import dsp.fsk.LTRFSKDecoder; -public class LTRStandardDecoder extends Decoder implements IRealBufferListener +public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBufferListener { public static final int LTR_STANDARD_MESSAGE_LENGTH = 40; @@ -36,6 +36,10 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener private MessageFramer mLTRMessageFramer; private LTRStandardMessageProcessor mLTRMessageProcessor; + /** + * LTR Decoder. Decodes unfiltered (e.g. demodulated but with no DC or + * audio filtering) samples and produces LTR Standard messages. + */ public LTRStandardDecoder( AliasList aliasList, MessageDirection direction ) { @@ -68,7 +72,7 @@ public class LTRStandardDecoder extends Decoder implements IRealBufferListener } @Override - public Listener getRealBufferListener() + public Listener getUnFilteredRealBufferListener() { return mLTRFSKDecoder; } diff --git a/src/module/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java index 86289146..7e973af2 100644 --- a/src/module/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -30,7 +30,7 @@ import java.util.List; import module.decode.Decoder; import module.decode.DecoderType; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -46,7 +46,7 @@ import dsp.fsk.FSK2Decoder.Output; * MDC1200 Decoder - 1200 baud 2FSK decoder that can process 48k sample rate * complex or floating point samples and output fully framed MDC1200 messages */ -public class MDCDecoder extends Decoder implements IRealBufferListener, +public class MDCDecoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -197,7 +197,7 @@ public class MDCDecoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } diff --git a/src/module/decode/mpt1327/MPT1327ConfigEditor.java b/src/module/decode/mpt1327/MPT1327ConfigEditor.java index 01c2af35..fa036c96 100644 --- a/src/module/decode/mpt1327/MPT1327ConfigEditor.java +++ b/src/module/decode/mpt1327/MPT1327ConfigEditor.java @@ -31,7 +31,7 @@ import module.decode.DecodeEditor; import module.decode.config.DecodeConfiguration; import module.decode.mpt1327.MPT1327Decoder.Sync; import playlist.PlaylistManager; -import controller.channel.ChannelNode; +import controller.channel.Channel; import controller.channel.map.ChannelMap; public class MPT1327ConfigEditor extends DecodeEditor @@ -44,16 +44,16 @@ public class MPT1327ConfigEditor extends DecodeEditor private JSlider mCallTimeout; private JLabel mTrafficChannelPoolSizeLabel; private JSlider mTrafficChannelPoolSize; - private ChannelNode mChannelNode; + private Channel mChannel; private PlaylistManager mPlaylistManager; public MPT1327ConfigEditor( DecodeConfiguration config, - ChannelNode channelNode, + Channel channel, PlaylistManager playlistManager ) { super( config ); - mChannelNode = channelNode; + mChannel = channel; mPlaylistManager = playlistManager; initGUI(); @@ -140,7 +140,7 @@ public class MPT1327ConfigEditor extends DecodeEditor add( mTrafficChannelPoolSizeLabel ); add( mTrafficChannelPoolSize, "wrap,grow" ); - reset(); +// reset(); } @Override @@ -167,36 +167,35 @@ public class MPT1327ConfigEditor extends DecodeEditor config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); } - @Override - public void reset() - { - DecodeConfigMPT1327 config = (DecodeConfigMPT1327)mConfig; - - String name = config.getChannelMapName(); - - ChannelMap selected = null; - - if( name != null ) - { - for( ChannelMap map: mChannelNode.getModel().getResourceManager() - .getPlaylistManager().getPlayist().getChannelMapList() - .getChannelMap() ) - { - if( map.getName().equalsIgnoreCase( name ) ) - { - selected = map; - } - } - } - - mComboChannelMaps.setSelectedItem( selected ); - mComboChannelMaps.requestFocus(); - mComboChannelMaps.requestFocusInWindow(); - - mComboSync.setSelectedItem( config.getSync() ); - - mCallTimeout.setValue( config.getCallTimeout() ); - - mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); - } +// @Override +// public void reset() +// { +// DecodeConfigMPT1327 config = (DecodeConfigMPT1327)mConfig; +// +// String name = config.getChannelMapName(); +// +// ChannelMap selected = null; +// +// if( name != null ) +// { +// for( ChannelMap map: mPlaylistManager.getPlayist().getChannelMapList() +// .getChannelMap() ) +// { +// if( map.getName().equalsIgnoreCase( name ) ) +// { +// selected = map; +// } +// } +// } +// +// mComboChannelMaps.setSelectedItem( selected ); +// mComboChannelMaps.requestFocus(); +// mComboChannelMaps.requestFocusInWindow(); +// +// mComboSync.setSelectedItem( config.getSync() ); +// +// mCallTimeout.setValue( config.getCallTimeout() ); +// +// mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); +// } } diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java index 679da857..799fd5a0 100644 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -35,7 +35,7 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -55,7 +55,7 @@ import dsp.fsk.FSK2Decoder.Output; * traffic messages. */ public class MPT1327Decoder extends Decoder - implements IRealBufferListener, Instrumentable + implements IFilteredRealBufferListener, Instrumentable { private final static Logger mLog = LoggerFactory.getLogger( MPT1327Decoder.class ); @@ -146,7 +146,7 @@ public class MPT1327Decoder extends Decoder } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } diff --git a/src/module/decode/p25/C4FMSymbolFilter.java b/src/module/decode/p25/C4FMSymbolFilter.java index 0c88b18a..651f2c60 100644 --- a/src/module/decode/p25/C4FMSymbolFilter.java +++ b/src/module/decode/p25/C4FMSymbolFilter.java @@ -16,12 +16,13 @@ import sample.Listener; import sample.real.RealBuffer; import sample.real.RealSampleListener; import sample.real.RealSampleProvider; -import source.tuner.frequency.FrequencyCorrectionControl; -import source.tuner.frequency.FrequencyCorrectionControl.FrequencyCorrectionResetListener; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeListener; import dsp.gain.DirectGainControl; -public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, - Listener, +public class C4FMSymbolFilter implements Listener, + IFrequencyChangeListener, RealSampleProvider, Instrumentable { @@ -201,15 +202,17 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, private float mHistory[] = new float[ NUMBER_FILTER_TAPS ]; private int mHistoryLast = 0; - private boolean mResetFrequencyTracker = false; - private RealSampleListener mListener; private DirectGainControl mGainController = new DirectGainControl( 15.0f, 0.1f, 35.0f, 0.3f ); - private FrequencyCorrectionControl mFrequencyCorrectionControl; private int mFrequencyAdjustmentRequested = 0; + private int mFrequencyCorrection = 0; + private int mFrequencyCorrectionMaximum = 3000; + private boolean mResetFrequencyTracker = false; + private FrequencyCorrectionProcessor mFrequencyCorrectionProcessor; + private Listener mFrequencyChangeListener; /** * C4FM Symbol Filter @@ -224,18 +227,15 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, * setting for this filter is 15.4 and that yields a symbol spread that * is centered on 2.0, ranging 1.92 to 2.08. */ - public C4FMSymbolFilter( FrequencyCorrectionControl frequencyControl ) + public C4FMSymbolFilter( int frequencyCorrectionMaximum ) { - mFrequencyCorrectionControl = frequencyControl; - - /* Register for reset events, so we can reset internal frequency tracking */ - mFrequencyCorrectionControl.setListener( this ); + mFrequencyCorrectionMaximum = frequencyCorrectionMaximum; } public void dispose() { mGainController = null; - mFrequencyCorrectionControl = null; +// mFrequencyCorrectionControl = null; mListener = null; } @@ -253,13 +253,21 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, * the internal frequency correction tracker */ if( mFrequencyAdjustmentRequested != 0 ) { - mFrequencyCorrectionControl.adjust( mFrequencyAdjustmentRequested ); + int correction = mFrequencyCorrection + mFrequencyAdjustmentRequested; + + if( correction > mFrequencyCorrectionMaximum ) + { + correction = mFrequencyCorrectionMaximum; + } + else if( correction < -mFrequencyCorrectionMaximum ) + { + correction = -mFrequencyCorrectionMaximum; + } + + broadcast( new FrequencyChangeEvent( + Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) ); mFrequencyAdjustmentRequested = 0; - - //Reset internal frequency tracking - mCoarseFrequencyCorrection = 0.0f; - mFineFrequencyCorrection = 0.0f; } } @@ -272,7 +280,8 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, mCoarseFrequencyCorrection = 0.0f; mFineFrequencyCorrection = 0.0f; - mFrequencyCorrectionControl.setFrequencyCorrection( 0 ); + broadcast( new FrequencyChangeEvent( + Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, 0 )); mResetFrequencyTracker = false; } @@ -397,17 +406,6 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, } } - /** - * Listener interface for frequency correction reset events. We reset the - * coarse and fine frequency control values when the tuner channel source - * has changed frequency (PPM) or sample rate - */ - @Override - public void resetFrequencyCorrection() - { - mResetFrequencyTracker = true; - } - @Override public void setListener( RealSampleListener listener ) { @@ -459,4 +457,56 @@ public class C4FMSymbolFilter implements FrequencyCorrectionResetListener, mSymbolSpreadTap = null; } } + + public void broadcast( FrequencyChangeEvent event ) + { + if( mFrequencyChangeListener != null ) + { + mFrequencyChangeListener.receive( event ); + } + } + + public void setFrequencyChangeListener( Listener listener ) + { + mFrequencyChangeListener = listener; + } + + @Override + public Listener getFrequencyChangeListener() + { + if( mFrequencyCorrectionProcessor == null ) + { + mFrequencyCorrectionProcessor = new FrequencyCorrectionProcessor(); + } + + return mFrequencyCorrectionProcessor; + } + + /** + * Receives notifications that the channel frequency correction has been + * applied and updates internal tracking value. + */ + public class FrequencyCorrectionProcessor implements Listener + { + @Override + public void receive( FrequencyChangeEvent event ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE: + mFrequencyCorrection = event.getValue().intValue(); + + //Reset internal frequency tracking + mCoarseFrequencyCorrection = 0.0f; + mFineFrequencyCorrection = 0.0f; + break; + case NOTIFICATION_FREQUENCY_CORRECTION_CHANGE: + case NOTIFICATION_SAMPLE_RATE_CHANGE: + mResetFrequencyTracker = true; + break; + default: + break; + } + } + } } diff --git a/src/module/decode/p25/P25DecodeEditor.java b/src/module/decode/p25/P25DecodeEditor.java index e3c7052d..c87d1e7c 100644 --- a/src/module/decode/p25/P25DecodeEditor.java +++ b/src/module/decode/p25/P25DecodeEditor.java @@ -34,8 +34,8 @@ import org.slf4j.LoggerFactory; import source.SourceEditor; import source.tuner.TunerEditor; -import controller.Editor; -import controller.channel.ChannelValidationException; +import controller.channel.ChannelConfigurationEditor; +import controller.channel.ConfigurationValidationException; public class P25DecodeEditor extends DecodeEditor { @@ -101,29 +101,29 @@ public class P25DecodeEditor extends DecodeEditor add( new JLabel( "Ignore Data Calls" ) ); add( mIgnoreDataCalls, "wrap,grow" ); - reset(); +// reset(); } - /** - * Enforce source=tuner for CQPSK modulation - */ - @Override - public void validate( Editor editor ) throws ChannelValidationException - { - if( editor instanceof SourceEditor && - ((DecodeConfigP25Phase1)mConfig).getModulation() == Modulation.CQPSK ) - { - if( !( editor instanceof TunerEditor ) ) - { - throw new ChannelValidationException( - "

LSM Simulcast

" - + "

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" ); - - } - } - } +// /** +// * Enforce source=tuner for CQPSK modulation +// */ +// @Override +// public void validate( ChannelConfigurationEditor editor ) throws ConfigurationValidationException +// { +// if( editor instanceof SourceEditor && +// ((DecodeConfigP25Phase1)mConfig).getModulation() == Modulation.CQPSK ) +// { +// if( !( editor instanceof TunerEditor ) ) +// { +// throw new ConfigurationValidationException( +// "

LSM Simulcast

" +// + "

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" ); +// +// } +// } +// } @Override public void save() @@ -135,13 +135,13 @@ public class P25DecodeEditor extends DecodeEditor config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); } - @Override - public void reset() - { - DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; - - mComboModulation.setSelectedItem( config.getModulation() ); - mIgnoreDataCalls.setSelected( config.getIgnoreDataCalls() ); - mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); - } +// @Override +// public void reset() +// { +// DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; +// +// mComboModulation.setSelectedItem( config.getModulation() ); +// mIgnoreDataCalls.setSelected( config.getIgnoreDataCalls() ); +// mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); +// } } diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java index 674b00b9..ca46e81b 100644 --- a/src/module/decode/p25/P25Decoder.java +++ b/src/module/decode/p25/P25Decoder.java @@ -3,11 +3,9 @@ package module.decode.p25; import instrument.Instrumentable; import module.decode.Decoder; import module.decode.DecoderType; -import source.tuner.frequency.IFrequencyCorrectionController; import alias.AliasList; -public abstract class P25Decoder extends Decoder - implements IFrequencyCorrectionController, Instrumentable +public abstract class P25Decoder extends Decoder implements Instrumentable { private P25MessageProcessor mMessageProcessor; diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index c4fabf0a..3bae443d 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -24,22 +24,24 @@ import instrument.tap.stream.FloatBufferTap; import instrument.tap.stream.FloatTap; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; -import source.tuner.frequency.FrequencyCorrectionControl; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.IFrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProvider; import alias.AliasList; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.fir.real.RealFIRFilter_RB_RB; -public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener +public class P25_C4FMDecoder extends P25Decoder + implements IFrequencyChangeListener, IFrequencyChangeProvider,IFilteredRealBufferListener { private final static Logger mLog = LoggerFactory.getLogger( P25_C4FMDecoder.class ); @@ -48,11 +50,8 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener private static final String INSTRUMENT_C4FM_SYMBOL_FILTER_OUTPUT = "Tap Point: Symbol Filter Output"; private static final String INSTRUMENT_C4FM_SLICER_OUTPUT = "Tap Point: C4FM Slicer Output"; - private final static int MAXIMUM_FREQUENCY_CORRECTION = 3000; //Hertz, +/- - private List mAvailableTaps; private RealFIRFilter_RB_RB mC4FMPreFilter; - private FrequencyCorrectionControl mFrequencyCorrectionControl; private C4FMSymbolFilter mSymbolFilter; private C4FMSlicer mC4FMSlicer; private P25MessageFramer mMessageFramer; @@ -70,23 +69,18 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener * infrastructure and subscriber identities that will be included in each * decoded message */ - public P25_C4FMDecoder( AliasList aliasList ) + public P25_C4FMDecoder( AliasList aliasList, int frequencyCorrectionMaximum ) { super( aliasList ); /* Filter demodulated sample buffers */ float[] filter = FilterFactory.getLowPass( 48000, 2500, 4000, 80, WindowType.HANNING, true ); - mLog.debug( "Demod Filter tap count:" + filter.length + " coefficients:" + Arrays.toString( filter ) ); +// mLog.debug( "Demod Filter tap count:" + filter.length + " coefficients:" + Arrays.toString( filter ) ); mC4FMPreFilter = new RealFIRFilter_RB_RB( filter, 1.0f ); - /* Issue tuned frequency correction commands, remotely controlled by the - * downstream symbol filter */ - mFrequencyCorrectionControl = new FrequencyCorrectionControl( - MAXIMUM_FREQUENCY_CORRECTION ); - /* Shape gain and frequency offsets to optimize sample stream */ - mSymbolFilter = new C4FMSymbolFilter( mFrequencyCorrectionControl ); + mSymbolFilter = new C4FMSymbolFilter( frequencyCorrectionMaximum ); mC4FMPreFilter.setListener( mSymbolFilter ); /* Convert samples to symbols */ @@ -109,9 +103,6 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener mC4FMPreFilter.dispose(); mC4FMPreFilter = null; - mFrequencyCorrectionControl.dispose(); - mFrequencyCorrectionControl = null; - mSymbolFilter.dispose(); mSymbolFilter = null; @@ -128,19 +119,7 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener } @Override - public boolean hasFrequencyCorrectionControl() - { - return mFrequencyCorrectionControl != null; - } - - @Override - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return mFrequencyCorrectionControl; - } - - @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mC4FMPreFilter; } @@ -262,4 +241,33 @@ public class P25_C4FMDecoder extends P25Decoder implements IRealBufferListener // TODO Auto-generated method stub } + + @Override + public void setFrequencyChangeListener( Listener listener ) + { + if( mSymbolFilter != null ) + { + mSymbolFilter.setFrequencyChangeListener( listener ); + } + } + + @Override + public void removeFrequencyChangeListener() + { + if( mSymbolFilter != null ) + { + mSymbolFilter.setFrequencyChangeListener( null ); + } + } + + @Override + public Listener getFrequencyChangeListener() + { + if( mSymbolFilter != null ) + { + return mSymbolFilter.getFrequencyChangeListener(); + } + + return null; + } } diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index 21555ae9..0e72cba4 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -34,7 +34,6 @@ import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.ComplexBufferToStreamConverter; import sample.complex.IComplexBufferListener; -import source.tuner.frequency.FrequencyCorrectionControl; import alias.AliasList; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; @@ -119,18 +118,6 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener return Modulation.CQPSK; } - @Override - public boolean hasFrequencyCorrectionControl() - { - return false; - } - - @Override - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return null; - } - /** * Provides a list of instrumentation taps for monitoring internal processing */ diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java index b6d46a2b..e125541d 100644 --- a/src/module/decode/passport/PassportDecoder.java +++ b/src/module/decode/passport/PassportDecoder.java @@ -28,7 +28,7 @@ import module.decode.Decoder; import module.decode.DecoderType; import module.decode.config.DecodeConfiguration; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IUnFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -36,7 +36,7 @@ import bits.SyncPattern; import dsp.fsk.LTRFSKDecoder; public class PassportDecoder extends Decoder - implements IRealBufferListener, Instrumentable + implements IUnFilteredRealBufferListener, Instrumentable { public static final int PASSPORT_MESSAGE_LENGTH = 68; public static final int PASSPORT_SYNC_LENGTH = 9; @@ -47,8 +47,11 @@ public class PassportDecoder extends Decoder private List mAvailableTaps; - public PassportDecoder( DecodeConfiguration config, - AliasList aliasList ) + /** + * Passport Decoder. Decodes unfiltered (e.g. demodulated but with no DC or + * audio filtering) samples and produces Passport messages. + */ + public PassportDecoder( DecodeConfiguration config, AliasList aliasList ) { mPassportFSKDecoder = new LTRFSKDecoder(); @@ -64,7 +67,7 @@ public class PassportDecoder extends Decoder } @Override - public Listener getRealBufferListener() + public Listener getUnFilteredRealBufferListener() { return mPassportFSKDecoder; } diff --git a/src/module/decode/state/ChannelCollectionPanel.java b/src/module/decode/state/ChannelCollectionPanel.java index 685511d1..eae3db84 100644 --- a/src/module/decode/state/ChannelCollectionPanel.java +++ b/src/module/decode/state/ChannelCollectionPanel.java @@ -16,6 +16,8 @@ import org.slf4j.LoggerFactory; import playlist.PlaylistManager; import settings.SettingsManager; import controller.channel.Channel; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; /** * Channel Collection assembles a primary channel panel and any related decoder @@ -28,15 +30,21 @@ public class ChannelCollectionPanel extends JPanel private final static Logger mLog = LoggerFactory.getLogger( ChannelCollectionPanel.class ); + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; private List mChannelPanels = new ArrayList(); - public ChannelCollectionPanel( PlaylistManager playlistManager, + public ChannelCollectionPanel( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, SettingsManager settingsManager, Channel channel ) { + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mPlaylistManager = playlistManager; mSettingsManager = settingsManager; @@ -49,8 +57,8 @@ public class ChannelCollectionPanel extends JPanel setOpaque( false ); - ChannelStatePanel csp = new ChannelStatePanel( mPlaylistManager, - mSettingsManager, channel ); + ChannelStatePanel csp = new ChannelStatePanel( mChannelModel, + mChannelProcessingManager, mPlaylistManager, mSettingsManager, channel ); mChannelPanels.add( csp ); diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index aff58241..5cd8f9b8 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -43,6 +43,8 @@ import settings.SettingsManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; /** * Gui wrapper for the list of currently processing primary channels @@ -56,12 +58,18 @@ public class ChannelList extends JPanel implements ChannelEventListener private HashMap mDisplayedPanels = new HashMap(); + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; - public ChannelList( PlaylistManager playlistManager, + public ChannelList( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, SettingsManager settingsManager ) { + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mPlaylistManager = playlistManager; mSettingsManager = settingsManager; @@ -96,8 +104,8 @@ public class ChannelList extends JPanel implements ChannelEventListener { if( !mDisplayedPanels.containsKey( channel ) ) { - ChannelCollectionPanel panel = new ChannelCollectionPanel( - mPlaylistManager, mSettingsManager, channel ); + ChannelCollectionPanel panel = new ChannelCollectionPanel( mChannelModel, + mChannelProcessingManager, mPlaylistManager, mSettingsManager, channel ); add( panel, "wrap" ); diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java index 0559da06..4e626964 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelStatePanel.java @@ -14,6 +14,7 @@ import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JPanel; +import module.ProcessingChain; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,6 +32,11 @@ import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; +import controller.channel.Channel; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; +import controller.channel.ChannelUtils; + public class ChannelStatePanel extends JPanel implements Listener, SettingChangeListener { @@ -38,10 +44,6 @@ public class ChannelStatePanel extends JPanel private final static Logger mLog = LoggerFactory.getLogger( ChannelStatePanel.class ); - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; - private Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); private Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); @@ -75,7 +77,10 @@ public class ChannelStatePanel extends JPanel protected SettingsManager mSettingsManager; private Channel mChannel; + private ChannelState mChannelState; private List mDecoderPanels = new ArrayList<>(); + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; /** * Gui component to a channel state. Provides System, Site and channel @@ -83,21 +88,31 @@ public class ChannelStatePanel extends JPanel * * Updates color background according to current channel state. */ - public ChannelStatePanel( PlaylistManager playlistManager, + public ChannelStatePanel( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, SettingsManager settingsManager, Channel channel ) { + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mPlaylistManager = playlistManager; mSettingsManager = settingsManager; mSettingsManager.addListener( this ); mChannel = channel; - mChannel.getChannelState().setChangedAttributeListener( this ); + + ProcessingChain processingChain = + channelProcessingManager.getProcessingChain( mChannel ); + + mChannelState = processingChain.getChannelState(); + + mChannelState.setChangedAttributeListener( this ); init(); - for( DecoderState decoderState: getChannel().getProcessingChain().getDecoderStates() ) + for( DecoderState decoderState: processingChain.getDecoderStates() ) { DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoderState ); mDecoderPanels.add( panel ); @@ -126,8 +141,7 @@ public class ChannelStatePanel extends JPanel mChannelLabel.setForeground( mColorLabelDetails ); add( mChannelLabel, "wrap" ); - mStateLabel = new JLabel( mChannel.getChannelState().getState() - .getDisplayValue() ); + mStateLabel = new JLabel( mChannelState.getState().getDisplayValue() ); mStateLabel.setFont( mFontDecoder ); mStateLabel.setForeground( mColorLabelDecoder ); add( mStateLabel ); @@ -161,8 +175,6 @@ public class ChannelStatePanel extends JPanel mOptionsLabel.setFont( mFontDetails ); mOptionsLabel.setForeground( mColorLabelAuxDecoder ); add( mOptionsLabel, "wrap" ); - -// add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx" ); } public Channel getChannel() @@ -177,7 +189,8 @@ public class ChannelStatePanel extends JPanel mSettingsManager.removeListener( this ); mSettingsManager = null; - mChannel.getChannelState().removeChangedAttributeListener(); + mChannelState.removeChangedAttributeListener(); + mChannelState = null; mChannel = null; for( DecoderPanel panel: mDecoderPanels ) @@ -192,8 +205,6 @@ public class ChannelStatePanel extends JPanel @Override public void receive( final ChangedAttribute changedAttribute ) { - final ChannelState state = mChannel.getChannelState(); - EventQueue.invokeLater( new Runnable() { @Override @@ -202,9 +213,9 @@ public class ChannelStatePanel extends JPanel switch( changedAttribute ) { case CHANNEL_STATE: - if( state.getState() != null ) + if( mChannelState.getState() != null ) { - mStateLabel.setText( state.getState().getDisplayValue() ); + mStateLabel.setText( mChannelState.getState().getDisplayValue() ); } break; case SOURCE: @@ -214,7 +225,7 @@ public class ChannelStatePanel extends JPanel case CHANNEL_NAME: case SITE_NAME: case SYSTEM_NAME: - mChannelLabel.setText( mChannel.getChannelDisplayName() ); + mChannelLabel.setText( mChannel.toString() ); break; default: break; @@ -419,7 +430,7 @@ public class ChannelStatePanel extends JPanel public JMenu getContextMenu() { - return ChannelUtils.getContextMenu( mPlaylistManager, mChannel, - ChannelStatePanel.this ); + return ChannelUtils.getContextMenu( mChannelModel, mChannelProcessingManager, + mPlaylistManager, mChannel, ChannelStatePanel.this ); } } diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index 18d90d74..ec64ab84 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import module.Module; +import module.ProcessingChain; import module.decode.config.DecodeConfiguration; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; @@ -21,11 +22,12 @@ import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; import alias.Alias; import alias.priority.Priority; -import controller.ResourceManager; import controller.channel.Channel; import controller.channel.Channel.ChannelType; -import controller.site.Site; -import controller.system.System; +import controller.channel.ChannelEvent; +import controller.channel.ChannelEvent.Event; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener @@ -40,15 +42,14 @@ public class TrafficChannelManager extends Module private DecoderStateEventListener mEventListener = new DecoderStateEventListener(); private Listener mCallEventListener; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private DecodeConfiguration mDecodeConfiguration; private RecordConfiguration mRecordConfiguration; - private ResourceManager mResourceManager; private String mSystem; private String mSite; private String mAliasListName; - private long mTrafficChannelTimeout; private CallEvent mPreviousDoNotMonitorCallEvent; - private boolean mShuttingDown = false; /** * Monitors call events and allocates traffic decoder channels in response @@ -68,7 +69,8 @@ public class TrafficChannelManager extends Module * @param trafficChannelPoolSize - maximum number of allocated traffic channels * in the pool */ - public TrafficChannelManager( ResourceManager resourceManager, + public TrafficChannelManager( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, DecodeConfiguration decodeConfiguration, RecordConfiguration recordConfiguration, String system, @@ -77,13 +79,14 @@ public class TrafficChannelManager extends Module long trafficChannelTimeout, int trafficChannelPoolSize ) { - mResourceManager = resourceManager; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mDecodeConfiguration = decodeConfiguration; mRecordConfiguration = recordConfiguration; mSystem = system; mSite = site; mAliasListName = aliasListName; - mTrafficChannelTimeout = trafficChannelTimeout; +// mTrafficChannelTimeout = trafficChannelTimeout; mTrafficChannelPoolMaximumSize = trafficChannelPoolSize; } @@ -91,7 +94,6 @@ public class TrafficChannelManager extends Module public void dispose() { mCallEventListener = null; - mResourceManager = null; mDecodeConfiguration = null; mPreviousDoNotMonitorCallEvent = null; } @@ -115,7 +117,7 @@ public class TrafficChannelManager extends Module { for( Channel configuredChannel: mTrafficChannelPool ) { - if( !configuredChannel.isProcessing() ) + if( !configuredChannel.getEnabled() ) { channel = configuredChannel; break; @@ -130,12 +132,10 @@ public class TrafficChannelManager extends Module channel.setRecordConfiguration( mRecordConfiguration ); - channel.setResourceManager( mResourceManager ); - - channel.setTrafficChannelTimeout( mTrafficChannelTimeout ); - channel.setAliasListName( mAliasListName ); + mChannelModel.addChannel( channel ); + mTrafficChannelPool.add( channel ); } @@ -143,26 +143,15 @@ public class TrafficChannelManager extends Module if( channel != null ) { channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) ); - channel.setSystem( mSystem ); channel.setSite( mSite ); channel.setName( channelNumber ); - channel.setEnabled( true ); + mChannelModel.broadcast( new ChannelEvent( channel, + Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); - /* Check the channel obtained a source and is currently processing */ - if( channel.isProcessing() ) - { - mTrafficChannelsInUse.put( channelNumber, channel ); - } - else - { - channel.setEnabled( false ); - - channel.setSystem( mSystem ); - channel.setSite( mSite ); - channel.setName( "Traffic" ); - } + mChannelModel.broadcast( new ChannelEvent( channel, + Event.REQUEST_ENABLE ) ); } } @@ -210,26 +199,29 @@ public class TrafficChannelManager extends Module if( channel != null ) { - if( channel.isProcessing() ) + if( channel.getEnabled() ) { - callEvent.setDetails( "TRAFFIC CHANNEL ALLOCATED" ); - - ChannelState state = channel.getChannelState(); - - /* Register as a listener to be notified when the call - * is completed */ - state.configureAsTrafficChannel( - new TrafficChannelStatusListener( this, - callEvent.getChannel() ), event ); - - /* Set state to call so that audio squelch state is correct */ - state.setState( State.CALL ); + ProcessingChain chain = mChannelProcessingManager + .getProcessingChain( channel ); + + if( chain != null ) + { + ChannelState state = chain.getChannelState(); + + /* Register as a listener to be notified when the call + * is completed */ + state.configureAsTrafficChannel( + new TrafficChannelStatusListener( this, + callEvent.getChannel() ), event ); + + /* Set state to call so that audio squelch state is correct */ + state.setState( State.CALL ); + } } else { callEvent.setCallEventType( CallEventType.CALL_DETECT ); - callEvent.setDetails( "CHANNEL PROCESSING ERROR" ); - } + } } else { @@ -390,15 +382,13 @@ public class TrafficChannelManager extends Module { synchronized( mTrafficChannelsInUse ) { - if( channelNumber != null && - mTrafficChannelsInUse.containsKey( channelNumber ) ) + if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) ) { Channel channel = mTrafficChannelsInUse.get( channelNumber ); - + + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_DISABLE ) ); + mTrafficChannelsInUse.remove( channelNumber ); - - /* Disable the channel and broadcast a notification */ - channel.setEnabled( false ); } } } diff --git a/src/module/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java index 83ccf2aa..8f21e80d 100644 --- a/src/module/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -31,7 +31,7 @@ import module.decode.Decoder; import module.decode.DecoderType; import sample.Broadcaster; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import alias.AliasList; import bits.MessageFramer; @@ -45,7 +45,7 @@ import dsp.fsk.FSK2Decoder.Output; /** * TAIT 1200 - 1200 baud 2FSK decoder */ -public class Tait1200Decoder extends Decoder implements IRealBufferListener, +public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListener, Instrumentable { /* Decimated sample rate ( 48,000 / 2 = 24,000 ) feeding the decoder */ @@ -179,7 +179,7 @@ public class Tait1200Decoder extends Decoder implements IRealBufferListener, } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return mDecimationFilter; } diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java index 716c6677..8e311102 100644 --- a/src/module/demodulate/am/AMDemodulatorModule.java +++ b/src/module/demodulate/am/AMDemodulatorModule.java @@ -21,7 +21,7 @@ import module.Module; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; -import sample.real.IRealBufferProvider; +import sample.real.IFilteredRealBufferProvider; import sample.real.RealBuffer; import dsp.am.AMDemodulator_CB; import dsp.filter.FilterFactory; @@ -31,7 +31,7 @@ import dsp.filter.fir.real.RealFIRFilter_RB_RB; import dsp.gain.AutomaticGainControl_RB; public class AMDemodulatorModule extends Module - implements IComplexBufferListener, IRealBufferProvider + implements IComplexBufferListener, IFilteredRealBufferProvider { private static final int SAMPLE_RATE = 48000; @@ -99,13 +99,13 @@ public class AMDemodulatorModule extends Module } @Override - public void setRealBufferListener( Listener listener ) + public void setFilteredRealBufferListener( Listener listener ) { mAGC.setListener( listener ); } @Override - public void removeRealBufferListener() + public void removeFilteredRealBufferListener() { mAGC.removeListener(); } diff --git a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java new file mode 100644 index 00000000..c6535268 --- /dev/null +++ b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.demodulate.audio; + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.IFilteredRealBufferProvider; +import sample.real.IUnFilteredRealBufferListener; +import sample.real.RealBuffer; +import dsp.filter.FilterFactory; +import dsp.filter.Window.WindowType; +import dsp.filter.dc.DCRemovalFilter_RealBuffer; +import dsp.filter.fir.real.RealFIRFilter_RB_RB; + +public class DemodulatedAudioFilterModule extends Module + implements IUnFilteredRealBufferListener, IFilteredRealBufferProvider +{ + private final static Logger mLog = LoggerFactory.getLogger( DemodulatedAudioFilterModule.class ); + + private DCRemovalFilter_RealBuffer mDCFilter = new DCRemovalFilter_RealBuffer(); + private RealFIRFilter_RB_RB mBandPassFilter; + /** + * Filters demodulated audio and removes any DC component from the buffer + * + * @param pass - audio pass band pass frequency + * @param stop - audio pass band stop frequency + */ + public DemodulatedAudioFilterModule( int pass, int stop ) + { + assert( stop > pass ); + +//TODO: change this to a band pass filter + mBandPassFilter = new RealFIRFilter_RB_RB( + FilterFactory.getLowPass( 48000, pass, stop, 60, + WindowType.HANNING, true ), 1.0f ); + + mDCFilter.setListener( mBandPassFilter ); + } + + @Override + public void dispose() + { + mBandPassFilter.dispose(); + mBandPassFilter = null; + + mDCFilter.dispose(); + mDCFilter = null; + } + + @Override + public void reset() + { + } + + @Override + public void setFilteredRealBufferListener( Listener listener ) + { + mBandPassFilter.setListener( listener ); + } + + @Override + public void removeFilteredRealBufferListener() + { + mBandPassFilter.removeListener(); + } + + @Override + public void start() + { + } + + @Override + public void stop() + { + } + + @Override + public Listener getUnFilteredRealBufferListener() + { + return mDCFilter; + } +} diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java index 2d2afe16..da39a197 100644 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -22,43 +22,27 @@ import module.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Broadcaster; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; -import sample.real.IRealBufferProvider; +import sample.real.IUnFilteredRealBufferProvider; import sample.real.RealBuffer; -import source.tuner.frequency.AutomaticFrequencyControl_RB; -import source.tuner.frequency.FrequencyCorrectionControl; -import source.tuner.frequency.IFrequencyCorrectionController; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; -import dsp.filter.dc.DCRemovalFilter_RB; import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; import dsp.fm.FMDemodulator_CB; -public class FMDemodulatorModule extends Module implements IComplexBufferListener, - IFrequencyCorrectionController, IRealBufferProvider +public class FMDemodulatorModule extends Module + implements IComplexBufferListener, IUnFilteredRealBufferProvider { - private final static Logger mLog = LoggerFactory.getLogger( FMDemodulatorModule.class ); - - /* Determines responsiveness of DC filter to frequency changes */ - private static final float DC_REMOVAL_RATIO = 0.000003f; - private static final int SAMPLE_RATE = 48000; private ComplexFIRFilter_CB_CB mIQFilter; private FMDemodulator_CB mDemodulator; - private Broadcaster mUnfilteredBufferBroadcaster = new Broadcaster(); - private DCRemovalFilter_RB mDCRemovalFilter; - private AutomaticFrequencyControl_RB mAutomaticFrequencyControl; - private boolean mRemoveDC = true; - - private Listener mListener; /** - * FM Demodulator with integrated DC removal and automatic frequency - * correction control. + * FM Demodulator with I/Q filter. Demodulated output is unfiltered and + * may contain a DC component. * * @param pass - pass frequency for IQ filtering prior to demodulation. This * frequency should be half of the signal bandwidth since the filter will @@ -66,56 +50,16 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene * combined pass bandwidth will be twice this value. * * @param stop - stop frequency for IQ filtering prior to demodulation. - * - * @param maxFrequencyCorrection - defines the maximum frequency +/- that - * the correction controller can adjust from the center tuned frequency. - * Set this value to 0 for no frequency correction control. */ - public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection ) - { - this( pass, stop, maxFrequencyCorrection, true ); - } - - public FMDemodulatorModule( int pass, int stop, int maxFrequencyCorrection, boolean removeDC ) + public FMDemodulatorModule( int pass, int stop ) { assert( stop > pass ); - mRemoveDC = removeDC; - mIQFilter = new ComplexFIRFilter_CB_CB( FilterFactory.getLowPass( SAMPLE_RATE, pass, stop, 60, WindowType.HAMMING, true ), 1.0f ); mDemodulator = new FMDemodulator_CB( 1.0f ); mIQFilter.setListener( mDemodulator ); - - mUnfilteredBufferBroadcaster = new Broadcaster(); - mDemodulator.setListener( mUnfilteredBufferBroadcaster ); - - if( maxFrequencyCorrection > 0 ) - { - /* Frequency correction requires unfiltered samples in order to detect - * the DC component present in a mistuned FM demodulated sample stream */ - mAutomaticFrequencyControl = new AutomaticFrequencyControl_RB( maxFrequencyCorrection ); - mUnfilteredBufferBroadcaster.addListener( mAutomaticFrequencyControl ); - } - - if( mRemoveDC ) - { - mDCRemovalFilter = new DCRemovalFilter_RB( DC_REMOVAL_RATIO ); - mUnfilteredBufferBroadcaster.addListener( mDCRemovalFilter ); - } - } - - @Override - public boolean hasFrequencyCorrectionControl() - { - return mAutomaticFrequencyControl != null; - } - - @Override - public FrequencyCorrectionControl getFrequencyCorrectionControl() - { - return mAutomaticFrequencyControl; } @Override @@ -132,68 +76,24 @@ public class FMDemodulatorModule extends Module implements IComplexBufferListene mDemodulator.dispose(); mDemodulator = null; - - if( mDCRemovalFilter != null ) - { - mDCRemovalFilter.dispose(); - } - mDCRemovalFilter = null; - - if( mAutomaticFrequencyControl != null ) - { - mAutomaticFrequencyControl.dispose(); - mAutomaticFrequencyControl = null; - } - - if( mUnfilteredBufferBroadcaster != null ) - { - mUnfilteredBufferBroadcaster.dispose(); - mUnfilteredBufferBroadcaster = null; - } } @Override public void reset() { mDemodulator.reset(); - - if( mDCRemovalFilter != null ) - { - mDCRemovalFilter.reset(); - } - - if( mAutomaticFrequencyControl != null ) - { - mAutomaticFrequencyControl.reset(); - } } @Override - public void setRealBufferListener( Listener listener ) + public void setUnFilteredRealBufferListener( Listener listener ) { - mListener = listener; - - if( mRemoveDC ) - { - mDCRemovalFilter.setListener( listener ); - } - else - { - mUnfilteredBufferBroadcaster.addListener( listener ); - } + mDemodulator.setListener( listener ); } @Override - public void removeRealBufferListener() + public void removeUnFilteredRealBufferListener() { - if( mRemoveDC ) - { - mDCRemovalFilter.removeListener(); - } - else - { - mUnfilteredBufferBroadcaster.removeListener( mListener ); - } + mDemodulator.removeListener(); } @Override diff --git a/src/module/log/EventLogComponentEditor.java b/src/module/log/EventLogComponentEditor.java index e8fefbee..2a9e081a 100644 --- a/src/module/log/EventLogComponentEditor.java +++ b/src/module/log/EventLogComponentEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -24,7 +24,8 @@ import javax.swing.SwingUtilities; import module.log.config.EventLogConfiguration; import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; public class EventLogComponentEditor extends AbstractChannelEditor { @@ -34,9 +35,9 @@ public class EventLogComponentEditor extends AbstractChannelEditor private JCheckBox mDecodedLogger = new JCheckBox( "Decoded Messages" ); private JCheckBox mCallEventLogger = new JCheckBox( "Call Events" ); - public EventLogComponentEditor( ChannelNode channelNode ) + public EventLogComponentEditor( Channel channel ) { - super( channelNode ); + super( channel ); add( mBinaryLogger, "span" ); add( mDecodedLogger, "span" ); @@ -53,7 +54,7 @@ public class EventLogComponentEditor extends AbstractChannelEditor { SwingUtilities.invokeLater(new Runnable() { - final List mLoggers = mChannelNode.getChannel() + final List mLoggers = mChannel .getEventLogConfiguration().getLoggers(); @Override @@ -75,8 +76,7 @@ public class EventLogComponentEditor extends AbstractChannelEditor @Override public void save() { - EventLogConfiguration config = mChannelNode.getChannel() - .getEventLogConfiguration(); + EventLogConfiguration config = mChannel.getEventLogConfiguration(); config.clear(); @@ -94,4 +94,18 @@ public class EventLogComponentEditor extends AbstractChannelEditor config.addLogger( EventLogType.CALL_EVENT ); } } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/playlist/PlaylistEditor.java b/src/playlist/PlaylistEditor.java new file mode 100644 index 00000000..5d5d92a2 --- /dev/null +++ b/src/playlist/PlaylistEditor.java @@ -0,0 +1,88 @@ +package playlist; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.coderazzi.filters.gui.AutoChoices; +import net.coderazzi.filters.gui.TableFilterHeader; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceManager; + +import com.jidesoft.swing.JideSplitPane; + +import controller.channel.Channel; +import controller.channel.ChannelEditor; +import controller.channel.ChannelModel; + +public class PlaylistEditor extends JPanel implements ListSelectionListener +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( PlaylistEditor.class ); + + private ChannelModel mChannelModel; + private JTable mChannelTable; + private ChannelEditor mEditor; + + public PlaylistEditor( ChannelModel channelModel, + PlaylistManager playlistManager, + SourceManager sourceManager ) + { + mChannelModel = channelModel; + + mEditor = new ChannelEditor( channelModel, playlistManager, sourceManager ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", + "[grow,fill]", + "[grow,fill]") ); + + //System Configuration View and Editor + mChannelTable = new JTable( mChannelModel ); + mChannelTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mChannelTable.getSelectionModel().addListSelectionListener( this ); + mChannelTable.setAutoCreateRowSorter( true ); + + TableFilterHeader channelHeaderFilter = new TableFilterHeader( mChannelTable ); + channelHeaderFilter.setAutoChoices( AutoChoices.ENABLED ); + + JScrollPane channelScroller = new JScrollPane( mChannelTable ); + + JideSplitPane splitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + splitPane.setDividerSize( 5 ); + splitPane.add( channelScroller ); + splitPane.add( mEditor ); + + add( splitPane ); + } + + @Override + public void valueChanged( ListSelectionEvent event ) + { + //This limits event firing to only when selection is complete + if( !event.getValueIsAdjusting() ) + { + int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); + + mLog.debug( "Index:" + index ); + + Channel channel = mChannelModel.getChannels().get( index ); + + if( channel != null ) + { + mEditor.setChannel( channel ); + } + } + } +} diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index f23f7075..9d43c496 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -52,7 +52,6 @@ public class PlaylistManager public PlaylistManager( ChannelModel channelModel ) { mChannelModel = channelModel; - init(); } public Playlist getPlayist() @@ -73,7 +72,7 @@ public class PlaylistManager * Loads playlist from the current playlist file, or the default playlist file, * as specified in the current SDRTRunk system settings */ - private void init() + public void init() { SystemProperties props = SystemProperties.getInstance(); @@ -163,6 +162,75 @@ public class PlaylistManager } } + private void createBackupPlaylist() + { + SystemProperties props = SystemProperties.getInstance(); + + Path playlistPath = props.getApplicationFolder( "playlist" ); + + String playlistDefault = props.get( "playlist.defaultfilename", + "playlist.xml" ); + + String playlistCurrent = props.get( "playlist.currentfilename", + playlistDefault ); + + Path current = playlistPath.resolve( playlistCurrent ); + + if( Files.exists( current ) ) + { + int revision = 1; + + String playlistBackup = playlistCurrent.replace( ".xml", "_backup_" + revision + ".xml" ); + + Path filePath = playlistPath.resolve( playlistBackup ); + + while( Files.exists( filePath ) ) + { + revision++; + + playlistBackup = playlistCurrent.replace( ".xml", "_backup_" + revision + ".xml" ); + + filePath = playlistPath.resolve( playlistBackup ); + + if( revision > 10 ) + { + mLog.error( "Couldn't create playlist backup - maximum revisions exceeded" ); + return; + } + } + + File outputFile = new File( filePath.toString() ); + + try + { + if( !outputFile.exists() ) + { + outputFile.createNewFile(); + } + } + catch( Exception e ) + { + mLog.error( "PlaylistManager - couldn't create file to save " + + "playlist backup [" + filePath.toString() + "]" ); + } + + try( OutputStream out = new FileOutputStream( outputFile ) ) + { + Files.copy( current, out ); + + mLog.info( "Playlist backed up to:" + filePath ); + } + catch( IOException ioe ) + { + mLog.error( "Error copying playlist to backup file [" + filePath + "]" ); + } + } + else + { + mLog.error( "Couldn't create backup playlist - original playlist does not exist" ); + } + } + /** * Erases current playlist and loads playlist from the playlistPath filename, * if it exists. @@ -238,6 +306,8 @@ public class PlaylistManager { mLog.info( "Legacy playlist format detected - converting ..." ); + createBackupPlaylist(); + //Playlist version 1 to version 2 format conversion. In order to keep //backwards compatibility, transfer all of the System-Site-Channel //objects over to the new channel list format diff --git a/src/playlist/PlaylistNode.java b/src/playlist/PlaylistNode.java index 93fdc2a8..5d8e5302 100644 --- a/src/playlist/PlaylistNode.java +++ b/src/playlist/PlaylistNode.java @@ -19,10 +19,14 @@ package playlist; import javax.swing.JPanel; +import settings.SettingsManager; +import source.SourceManager; import alias.AliasDirectory; import alias.AliasDirectoryNode; import controller.BaseNode; import controller.EmptyEditor; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; import controller.channel.map.ChannelMapList; import controller.channel.map.ChannelMapListNode; import controller.system.SystemList; @@ -35,11 +39,27 @@ public class PlaylistNode extends BaseNode private ChannelMapListNode mChannelMapListNode; private SystemListNode mSystemListNode; - public PlaylistNode() - { + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; + private SourceManager mSourceManager; + + public PlaylistNode( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SettingsManager settingsManager, + SourceManager sourceManager ) + { super( null ); - } - + + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; + mSourceManager = sourceManager; + } + private void init() { /** @@ -58,7 +78,8 @@ public class PlaylistNode extends BaseNode getPlaylist().setAliasDirectory( directory ); } - mAliasDirectoryNode = new AliasDirectoryNode( directory ); + mAliasDirectoryNode = new AliasDirectoryNode( mPlaylistManager, + mSettingsManager, directory ); getModel().insertNodeInto( mAliasDirectoryNode, this, 0 ); @@ -74,7 +95,7 @@ public class PlaylistNode extends BaseNode channelMapList = new ChannelMapList(); } - mChannelMapListNode = new ChannelMapListNode( channelMapList ); + mChannelMapListNode = new ChannelMapListNode( mPlaylistManager, channelMapList ); getModel().insertNodeInto( mChannelMapListNode, this, 1 ); @@ -91,7 +112,8 @@ public class PlaylistNode extends BaseNode getPlaylist().setSystemList( list ); } - mSystemListNode = new SystemListNode( list ); + mSystemListNode = new SystemListNode( list, mChannelModel, + mChannelProcessingManager, mPlaylistManager, mSourceManager ); getModel().insertNodeInto( mSystemListNode, this, 2 ); @@ -112,8 +134,7 @@ public class PlaylistNode extends BaseNode public void loadPlaylist() { - Playlist playlist = - getModel().getResourceManager().getPlaylistManager().getPlayist(); + Playlist playlist = mPlaylistManager.getPlayist(); setUserObject( playlist ); diff --git a/src/record/RecordComponentEditor.java b/src/record/RecordComponentEditor.java index 435a6ccb..7c23a360 100644 --- a/src/record/RecordComponentEditor.java +++ b/src/record/RecordComponentEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -22,7 +22,8 @@ import javax.swing.JCheckBox; import record.config.RecordConfiguration; import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; public class RecordComponentEditor extends AbstractChannelEditor { @@ -32,9 +33,9 @@ public class RecordComponentEditor extends AbstractChannelEditor private JCheckBox mBasebandRecorder = new JCheckBox( "Baseband I/Q" ); private JCheckBox mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" ); - public RecordComponentEditor( ChannelNode channelNode ) + public RecordComponentEditor( Channel channel ) { - super( channelNode ); + super( channel ); add( mAudioRecorder, "span" ); add( mBasebandRecorder, "span" ); @@ -46,8 +47,7 @@ public class RecordComponentEditor extends AbstractChannelEditor @Override public void save() { - RecordConfiguration config = - getChannelNode().getChannel().getRecordConfiguration(); + RecordConfiguration config = getChannel().getRecordConfiguration(); config.clearRecorders(); @@ -76,8 +76,7 @@ public class RecordComponentEditor extends AbstractChannelEditor @Override public void run() { - RecordConfiguration config = - getChannelNode().getChannel().getRecordConfiguration(); + RecordConfiguration config = getChannel().getRecordConfiguration(); mAudioRecorder.setSelected( config.getRecorders() .contains( RecorderType.AUDIO ) ); @@ -90,4 +89,18 @@ public class RecordComponentEditor extends AbstractChannelEditor } }); } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/record/RecordEditor.java b/src/record/RecordEditor.java index 6d25832e..9913bf9e 100644 --- a/src/record/RecordEditor.java +++ b/src/record/RecordEditor.java @@ -18,9 +18,11 @@ package record; import record.config.RecordConfiguration; -import controller.Editor; +import controller.channel.Channel; +import controller.channel.ChannelConfigurationEditor; +import controller.channel.ConfigurationValidationException; -public class RecordEditor extends Editor +public class RecordEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; @@ -42,7 +44,16 @@ public class RecordEditor extends Editor } @Override - public void reset() - { - } + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java index 949ac241..b4bf4aad 100644 --- a/src/record/RecorderManager.java +++ b/src/record/RecorderManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,7 +20,6 @@ package record; import java.io.File; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.TimeUnit; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -33,7 +32,6 @@ import audio.AudioPacket; import audio.metadata.Metadata; import audio.metadata.MetadataType; import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; public class RecorderManager implements Listener { @@ -170,8 +168,7 @@ public class RecorderManager implements Listener /** * Constructs a baseband recorder for use in a processing chain. */ - public static ComplexBufferWaveRecorder getBasebandRecorder( - ThreadPoolManager threadPoolManager, String channelName ) + public ComplexBufferWaveRecorder getBasebandRecorder( String channelName ) { StringBuilder sb = new StringBuilder(); sb.append( SystemProperties.getInstance() @@ -180,7 +177,7 @@ public class RecorderManager implements Listener sb.append( channelName ); sb.append( "_baseband" ); - return new ComplexBufferWaveRecorder( threadPoolManager, + return new ComplexBufferWaveRecorder( mThreadPoolManager, AUDIO_SAMPLE_RATE, sb.toString() ); } } diff --git a/src/record/wave/RealBufferWaveRecorder.java b/src/record/wave/RealBufferWaveRecorder.java index 49b0f3b6..0ad0d4f7 100644 --- a/src/record/wave/RealBufferWaveRecorder.java +++ b/src/record/wave/RealBufferWaveRecorder.java @@ -34,7 +34,7 @@ import org.slf4j.LoggerFactory; import sample.ConversionUtils; import sample.Listener; -import sample.real.IRealBufferListener; +import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import util.TimeStamp; import controller.ThreadPoolManager; @@ -44,7 +44,7 @@ import controller.ThreadPoolManager.ThreadType; * WAVE audio recorder module for recording real sample buffers to a wave file */ public class RealBufferWaveRecorder extends Module - implements IRealBufferListener, Listener + implements IFilteredRealBufferListener, Listener { private final static Logger mLog = LoggerFactory.getLogger( RealBufferWaveRecorder.class ); @@ -151,7 +151,7 @@ public class RealBufferWaveRecorder extends Module } @Override - public Listener getRealBufferListener() + public Listener getFilteredRealBufferListener() { return this; } diff --git a/src/sample/real/IFilteredRealBufferListener.java b/src/sample/real/IFilteredRealBufferListener.java new file mode 100644 index 00000000..06928fda --- /dev/null +++ b/src/sample/real/IFilteredRealBufferListener.java @@ -0,0 +1,8 @@ +package sample.real; + +import sample.Listener; + +public interface IFilteredRealBufferListener +{ + public Listener getFilteredRealBufferListener(); +} diff --git a/src/sample/real/IFilteredRealBufferProvider.java b/src/sample/real/IFilteredRealBufferProvider.java new file mode 100644 index 00000000..e600d368 --- /dev/null +++ b/src/sample/real/IFilteredRealBufferProvider.java @@ -0,0 +1,9 @@ +package sample.real; + +import sample.Listener; + +public interface IFilteredRealBufferProvider +{ + public void setFilteredRealBufferListener( Listener listener ); + public void removeFilteredRealBufferListener(); +} diff --git a/src/sample/real/IRealBufferListener.java b/src/sample/real/IRealBufferListener.java deleted file mode 100644 index 6ef33052..00000000 --- a/src/sample/real/IRealBufferListener.java +++ /dev/null @@ -1,8 +0,0 @@ -package sample.real; - -import sample.Listener; - -public interface IRealBufferListener -{ - public Listener getRealBufferListener(); -} diff --git a/src/sample/real/IRealBufferProvider.java b/src/sample/real/IRealBufferProvider.java deleted file mode 100644 index 0b11debd..00000000 --- a/src/sample/real/IRealBufferProvider.java +++ /dev/null @@ -1,9 +0,0 @@ -package sample.real; - -import sample.Listener; - -public interface IRealBufferProvider -{ - public void setRealBufferListener( Listener listener ); - public void removeRealBufferListener(); -} diff --git a/src/sample/real/IUnFilteredRealBufferListener.java b/src/sample/real/IUnFilteredRealBufferListener.java new file mode 100644 index 00000000..bce0f168 --- /dev/null +++ b/src/sample/real/IUnFilteredRealBufferListener.java @@ -0,0 +1,8 @@ +package sample.real; + +import sample.Listener; + +public interface IUnFilteredRealBufferListener +{ + public Listener getUnFilteredRealBufferListener(); +} diff --git a/src/sample/real/IUnFilteredRealBufferProvider.java b/src/sample/real/IUnFilteredRealBufferProvider.java new file mode 100644 index 00000000..764a5aeb --- /dev/null +++ b/src/sample/real/IUnFilteredRealBufferProvider.java @@ -0,0 +1,9 @@ +package sample.real; + +import sample.Listener; + +public interface IUnFilteredRealBufferProvider +{ + public void setUnFilteredRealBufferListener( Listener listener ); + public void removeUnFilteredRealBufferListener(); +} diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index ddcc4fa9..df430aa6 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -386,6 +386,12 @@ public class SettingsManager } MapIcon mapIcon = mIcons.get( name ); + + if( mapIcon == null ) + { + mapIcon = new MapIcon( name, name ); + mIcons.put( name, mapIcon ); + } if( mapIcon != null ) { @@ -399,7 +405,7 @@ public class SettingsManager } else { - mResizedIcons.put( mergedName, mapIcon.getImageIcon() ); + return mapIcon.getImageIcon(); } } diff --git a/src/source/ComplexSource.java b/src/source/ComplexSource.java index eeb4084e..e7e1939c 100644 --- a/src/source/ComplexSource.java +++ b/src/source/ComplexSource.java @@ -6,8 +6,8 @@ import sample.complex.ComplexBuffer; public abstract class ComplexSource extends Source implements Provider { - public ComplexSource( String name ) + public ComplexSource() { - super( name, SampleType.COMPLEX ); + super( SampleType.COMPLEX ); } } diff --git a/src/source/EmptySourceEditor.java b/src/source/EmptySourceEditor.java index ad1c1669..04af856d 100644 --- a/src/source/EmptySourceEditor.java +++ b/src/source/EmptySourceEditor.java @@ -19,14 +19,15 @@ package source; import javax.swing.JLabel; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; import source.config.SourceConfiguration; -import controller.ResourceManager; public class EmptySourceEditor extends SourceEditor { private static final long serialVersionUID = 1L; - public EmptySourceEditor( ResourceManager resourceManager, + public EmptySourceEditor( SourceManager resourceManager, SourceConfiguration config ) { super( resourceManager, config ); @@ -43,4 +44,18 @@ public class EmptySourceEditor extends SourceEditor JLabel selectLabel = new JLabel( "Please select a source" ); add( selectLabel ); } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/source/FloatSource.java b/src/source/FloatSource.java deleted file mode 100644 index c8161a9c..00000000 --- a/src/source/FloatSource.java +++ /dev/null @@ -1,12 +0,0 @@ -package source; - -import sample.Provider; -import sample.SampleType; - -public abstract class FloatSource extends Source implements Provider -{ - public FloatSource( String name ) - { - super( name, SampleType.REAL ); - } -} diff --git a/src/source/RealSource.java b/src/source/RealSource.java index 6b09ccd5..b512bfbc 100644 --- a/src/source/RealSource.java +++ b/src/source/RealSource.java @@ -7,8 +7,8 @@ import sample.real.RealBuffer; public abstract class RealSource extends Source implements Provider { - public RealSource( String name ) + public RealSource() { - super( name, SampleType.REAL ); + super( SampleType.REAL ); } } diff --git a/src/source/Source.java b/src/source/Source.java index 064cc6ce..217def00 100644 --- a/src/source/Source.java +++ b/src/source/Source.java @@ -17,27 +17,21 @@ ******************************************************************************/ package source; +import module.Module; import sample.SampleType; /** * Abstract class to define the minimum functionality of a sample data provider. */ -public abstract class Source +public abstract class Source extends Module { - protected String mName; protected SampleType mSampleType; - public Source( String name, SampleType sampleType ) + public Source( SampleType sampleType ) { - mName = name; mSampleType = sampleType; } - public String getName() - { - return mName; - } - public abstract int getSampleRate() throws SourceException; public abstract long getFrequency() throws SourceException; diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java index c4d4e4a2..b4f3673a 100644 --- a/src/source/SourceComponentEditor.java +++ b/src/source/SourceComponentEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -29,7 +29,8 @@ import net.miginfocom.swing.MigLayout; import source.config.SourceConfigFactory; import source.config.SourceConfiguration; import controller.channel.AbstractChannelEditor; -import controller.channel.ChannelNode; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; public class SourceComponentEditor extends AbstractChannelEditor { @@ -37,10 +38,13 @@ public class SourceComponentEditor extends AbstractChannelEditor private JComboBox mComboSources; private SourceEditor mEditor; + private SourceManager mSourceManager; - public SourceComponentEditor( ChannelNode channelNode ) + public SourceComponentEditor( SourceManager sourceManager, Channel channel ) { - super( channelNode ); + super( channel ); + + mSourceManager = sourceManager; setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); @@ -57,11 +61,9 @@ public class SourceComponentEditor extends AbstractChannelEditor { SourceConfiguration config; - if( selected == mChannelNode.getChannel() - .getSourceConfiguration().getSourceType() ) + if( selected == mChannel.getSourceConfiguration().getSourceType() ) { - config = mChannelNode.getChannel() - .getSourceConfiguration(); + config = mChannel.getSourceConfiguration(); } else { @@ -76,8 +78,7 @@ public class SourceComponentEditor extends AbstractChannelEditor } //Change to the new one - mEditor = SourceEditorFactory.getPanel( - mChannelNode.getModel().getResourceManager(), config ); + mEditor = mSourceManager.getPanel( config ); //Add it to the jpanel add( mEditor, "span 2" ); @@ -105,10 +106,10 @@ public class SourceComponentEditor extends AbstractChannelEditor { @Override - public void run() { + public void run() + { mComboSources.setSelectedItem( - mChannelNode.getChannel() - .getSourceConfiguration().getSourceType() ); + mChannel.getSourceConfiguration().getSourceType() ); mComboSources.requestFocus(); @@ -123,7 +124,20 @@ public class SourceComponentEditor extends AbstractChannelEditor mEditor.save(); //Let the calling save method send the config change event - mChannelNode.getChannel() - .setSourceConfiguration( mEditor.getConfig() ); + mChannel.setSourceConfiguration( mEditor.getConfig() ); } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/source/SourceEditor.java b/src/source/SourceEditor.java index dd5ad11e..1cb15a64 100644 --- a/src/source/SourceEditor.java +++ b/src/source/SourceEditor.java @@ -18,19 +18,18 @@ package source; import source.config.SourceConfiguration; -import controller.Editor; -import controller.ResourceManager; +import controller.channel.ChannelConfigurationEditor; -public abstract class SourceEditor extends Editor +public abstract class SourceEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; - protected ResourceManager mResourceManager; + protected SourceManager mSourceManager; protected SourceConfiguration mConfig; - public SourceEditor( ResourceManager resourceManager, SourceConfiguration config ) + public SourceEditor( SourceManager sourceManager, SourceConfiguration config ) { - mResourceManager = resourceManager; + mSourceManager = sourceManager; mConfig = config; } @@ -38,9 +37,9 @@ public abstract class SourceEditor extends Editor { return mConfig; } - - public ResourceManager getResourceManager() + + protected SourceManager getSourceManager() { - return mResourceManager; + return mSourceManager; } } diff --git a/src/source/SourceEditorFactory.java b/src/source/SourceEditorFactory.java deleted file mode 100644 index b918f79f..00000000 --- a/src/source/SourceEditorFactory.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source; - -import source.config.SourceConfiguration; -import source.mixer.MixerEditor; -import source.recording.RecordingEditor; -import source.tuner.TunerEditor; -import controller.ResourceManager; - -public class SourceEditorFactory -{ - public static SourceEditor getPanel( ResourceManager resourceManager, - SourceConfiguration config ) - { - SourceEditor configuredPanel; - - switch( config.getSourceType() ) - { - case MIXER: - configuredPanel = new MixerEditor( resourceManager, config ); - break; - case TUNER: - configuredPanel = new TunerEditor( resourceManager, config ); - break; - case RECORDING: - configuredPanel = new RecordingEditor( resourceManager, config ); - break; - case NONE: - default: - configuredPanel = new EmptySourceEditor( resourceManager, config ); - break; - } - - return configuredPanel; - } -} diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index 8c783ab9..9fab09f4 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -17,27 +17,43 @@ ******************************************************************************/ package source; +import settings.SettingsManager; import source.config.SourceConfiguration; +import source.mixer.MixerEditor; import source.mixer.MixerManager; -import controller.ResourceManager; +import source.recording.RecordingEditor; +import source.recording.RecordingSourceManager; +import source.tuner.TunerEditor; +import source.tuner.TunerManager; +import controller.ThreadPoolManager; -public class SourceManager { - /** - * NOTE: each source will auto-start once a listener registers on it to - * receive samples and should auto-stop once a listener de-registers - * - * Each channel object will handle the starting and stopping of each channel - * processing chain. - * - * Source Manager will reach out to the MixerManager or the TunerManager - * to get source objects to pass off to the channel object, as requested. - */ - - private ResourceManager mResourceManager; +public class SourceManager +{ + private MixerManager mMixerManager; + private RecordingSourceManager mRecordingSourceManager; + private TunerManager mTunerManager; - public SourceManager( ResourceManager resourceManager ) + public SourceManager( SettingsManager settingsManager, + ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; + mMixerManager = new MixerManager(); + mRecordingSourceManager = new RecordingSourceManager( settingsManager, threadPoolManager ); + mTunerManager = new TunerManager( mMixerManager, settingsManager, threadPoolManager ); + } + + public MixerManager getMixerManager() + { + return mMixerManager; + } + + public RecordingSourceManager getRecordingSourceManager() + { + return mRecordingSourceManager; + } + + public TunerManager getTunerManager() + { + return mTunerManager; } public Source getSource( SourceConfiguration config, int bandwidth ) @@ -48,14 +64,13 @@ public class SourceManager { switch( config.getSourceType() ) { case MIXER: - retVal = MixerManager.getInstance().getSource( config ); + retVal = mMixerManager.getSource( config ); break; case TUNER: - retVal = mResourceManager.getTunerManager().getSource( config, bandwidth ); + retVal = mTunerManager.getSource( config, bandwidth ); break; case RECORDING: - retVal = mResourceManager.getRecordingSourceManager() - .getSource( config, bandwidth ); + retVal = mRecordingSourceManager.getSource( config, bandwidth ); case NONE: default: break; @@ -63,4 +78,28 @@ public class SourceManager { return retVal; } + + public SourceEditor getPanel( SourceConfiguration config ) + { + SourceEditor configuredPanel; + + switch( config.getSourceType() ) + { + case MIXER: + configuredPanel = new MixerEditor( this, config ); + break; + case TUNER: + configuredPanel = new TunerEditor( this, config ); + break; + case RECORDING: + configuredPanel = new RecordingEditor( this, config ); + break; + case NONE: + default: + configuredPanel = new EmptySourceEditor( this, config ); + break; + } + + return configuredPanel; + } } diff --git a/src/source/mixer/MixerEditor.java b/src/source/mixer/MixerEditor.java index 3782213b..075068b6 100644 --- a/src/source/mixer/MixerEditor.java +++ b/src/source/mixer/MixerEditor.java @@ -24,11 +24,13 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; import source.SourceEditor; +import source.SourceManager; import source.config.SourceConfigMixer; import source.config.SourceConfiguration; import source.mixer.MixerManager.InputMixerConfiguration; -import controller.ResourceManager; public class MixerEditor extends SourceEditor { @@ -38,10 +40,10 @@ public class MixerEditor extends SourceEditor protected Mixer.Info mSelectedMixer = null; - public MixerEditor( ResourceManager resourceManager, + public MixerEditor( SourceManager sourceManager, SourceConfiguration config ) { - super( resourceManager, config ); + super( sourceManager, config ); initGUI(); } @@ -105,7 +107,8 @@ public class MixerEditor extends SourceEditor @Override public void run() { - InputMixerConfiguration[] mixers = MixerManager.getInstance().getInputMixers(); + InputMixerConfiguration[] mixers = mSourceManager + .getMixerManager().getInputMixers(); mComboMixers.setModel( new DefaultComboBoxModel( mixers ) ); @@ -155,4 +158,18 @@ public class MixerEditor extends SourceEditor } }); } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/source/mixer/MixerManager.java b/src/source/mixer/MixerManager.java index 298e760d..d7af001a 100644 --- a/src/source/mixer/MixerManager.java +++ b/src/source/mixer/MixerManager.java @@ -49,27 +49,15 @@ public class MixerManager private final static Logger mLog = LoggerFactory.getLogger( MixerManager.class ); - private static MixerManager sInstance = null; - private List mInputMixers = new ArrayList<>(); private List mOutputMixers = new ArrayList<>(); private HashMap mMixerTuners = new HashMap<>(); - private MixerManager() + public MixerManager() { loadMixers(); } - public static MixerManager getInstance() - { - if( sInstance == null ) - { - sInstance = new MixerManager(); - } - - return sInstance; - } - public RealMixerSource getSource( SourceConfiguration config ) { RealMixerSource retVal = null; @@ -108,7 +96,6 @@ public class MixerManager { return new RealMixerSource( dataLine, AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, - mixerName, new ShortAdapter() ); } } @@ -137,7 +124,7 @@ public class MixerManager { return new RealMixerSource( dataLine, AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, - mixerName, new ChannelShortAdapter( + new ChannelShortAdapter( mixerConfig.getChannel() ) ); } } diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 58b8d953..240e55bb 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -72,18 +72,30 @@ public class RealMixerSource extends RealSource * the left/right stereo channels are inverted. */ public RealMixerSource( TargetDataLine targetDataLine, - AudioFormat format, - String name, - ISampleAdapter sampleAdapter ) + AudioFormat format, + ISampleAdapter sampleAdapter ) { - super( name ); - mTargetDataLine = targetDataLine; mAudioFormat = format; mSampleAdapter = sampleAdapter; } - public void setListener( Listener listener ) + @Override + public void reset() + { + } + + @Override + public void start() + { + } + + @Override + public void stop() + { + } + + public void setListener( Listener listener ) { mSampleListeners.add( listener ); @@ -92,7 +104,7 @@ public class RealMixerSource extends RealSource { Thread thread = new Thread( mBufferReader ); thread.setDaemon( true ); - thread.setName( getName() + " Sample Reader" ); + thread.setName( "Sample Reader" ); thread.start(); } } diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index 90fad405..a3b1aba7 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -4,21 +4,20 @@ import java.io.File; import java.util.ArrayList; import java.util.concurrent.RejectedExecutionException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerChannel; -import source.tuner.TunerChannelProvider; +import source.tuner.ITunerChannelProvider; import source.tuner.TunerChannelSource; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; -import controller.ResourceManager; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.ThreadPoolManager; public class Recording implements Comparable, - FrequencyChangeListener, - TunerChannelProvider + IFrequencyChangeProcessor, + ITunerChannelProvider { - private ResourceManager mResourceManager; private RecordingConfiguration mConfiguration; private ArrayList mTunerChannels = @@ -26,10 +25,12 @@ public class Recording implements Comparable, private long mCenterFrequency; - public Recording( ResourceManager resourceManager, + private SettingsManager mSettingsManager; + + public Recording( SettingsManager settingsManager, RecordingConfiguration configuration ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mConfiguration = configuration; mCenterFrequency = mConfiguration.getCenterFrequency(); } @@ -37,7 +38,7 @@ public class Recording implements Comparable, public void setAlias( String alias ) { mConfiguration.setAlias( alias ); - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } public void setRecordingFile( File file ) throws SourceException @@ -50,7 +51,7 @@ public class Recording implements Comparable, } mConfiguration.setFilePath( file.getAbsolutePath() ); - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } /** @@ -90,13 +91,13 @@ public class Recording implements Comparable, @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { long frequency = event.getValue().longValue(); mConfiguration.setCenterFrequency( frequency ); - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); mCenterFrequency = frequency; diff --git a/src/source/recording/RecordingEditor.java b/src/source/recording/RecordingEditor.java index aabec88f..a6be5cbe 100644 --- a/src/source/recording/RecordingEditor.java +++ b/src/source/recording/RecordingEditor.java @@ -26,11 +26,13 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.SwingUtilities; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; import source.SourceEditor; +import source.SourceManager; import source.config.SourceConfigRecording; import source.config.SourceConfigTuner; import source.config.SourceConfiguration; -import controller.ResourceManager; public class RecordingEditor extends SourceEditor { @@ -38,10 +40,9 @@ public class RecordingEditor extends SourceEditor private JComboBox mComboRecordings; private JFrequencyControl mFrequencyControl; - public RecordingEditor( ResourceManager resourceManager, - SourceConfiguration config ) + public RecordingEditor( SourceManager sourceManager, SourceConfiguration config ) { - super( resourceManager, config ); + super( sourceManager, config ); initGUI(); } @@ -92,7 +93,7 @@ public class RecordingEditor extends SourceEditor @Override public void run() { - List recordings = getResourceManager() + List recordings = getSourceManager() .getRecordingSourceManager().getRecordings(); mComboRecordings.setModel( @@ -105,7 +106,7 @@ public class RecordingEditor extends SourceEditor if( recordingAlias != null ) { - Recording selected = mResourceManager + Recording selected = mSourceManager .getRecordingSourceManager() .getRecordingFromAlias( recordingAlias ); @@ -117,4 +118,18 @@ public class RecordingEditor extends SourceEditor } }); } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/source/recording/RecordingGroupNode.java b/src/source/recording/RecordingGroupNode.java index 8eb2029a..3e9d4631 100644 --- a/src/source/recording/RecordingGroupNode.java +++ b/src/source/recording/RecordingGroupNode.java @@ -24,17 +24,28 @@ import java.util.List; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; +import settings.SettingsManager; +import source.SourceManager; import controller.BaseNode; public class RecordingGroupNode extends BaseNode { private static final long serialVersionUID = 1L; - - public RecordingGroupNode() + + private SettingsManager mSettingsManager; + + public RecordingGroupNode( SourceManager sourceManager, SettingsManager settingsManager ) { - super( null ); + super( sourceManager ); + + mSettingsManager = settingsManager; } + private SourceManager getSourceManager() + { + return (SourceManager)getUserObject(); + } + public String toString() { return "Recordings [" + this.getChildCount() + "]"; @@ -53,13 +64,12 @@ public class RecordingGroupNode extends BaseNode { RecordingConfiguration config = new RecordingConfiguration(); - getModel().getResourceManager().getSettingsManager() - .addRecordingConfiguration( config ); + mSettingsManager.addRecordingConfiguration( config ); - Recording recording = getModel().getResourceManager() + Recording recording = getSourceManager() .getRecordingSourceManager().addRecording( config ); - RecordingNode node = new RecordingNode( recording ); + RecordingNode node = new RecordingNode( getSourceManager(), recording ); getModel().addNode( node, RecordingGroupNode.this, 0 ); @@ -76,12 +86,12 @@ public class RecordingGroupNode extends BaseNode public void loadRecordings() { - List recordings = getModel().getResourceManager() + List recordings = getSourceManager() .getRecordingSourceManager().getRecordings(); for( Recording recording: recordings ) { - RecordingNode node = new RecordingNode( recording ); + RecordingNode node = new RecordingNode( getSourceManager(), recording ); getModel().addNode( node, this, 0 ); } diff --git a/src/source/recording/RecordingNode.java b/src/source/recording/RecordingNode.java index 54ec12fd..920602e3 100644 --- a/src/source/recording/RecordingNode.java +++ b/src/source/recording/RecordingNode.java @@ -8,6 +8,7 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; +import source.SourceManager; import controller.BaseNode; public class RecordingNode extends BaseNode @@ -15,8 +16,10 @@ public class RecordingNode extends BaseNode private static final long serialVersionUID = 1L; private JPanel mEditor; + + private SourceManager mSourceManager; - public RecordingNode( Recording recording ) + public RecordingNode( SourceManager sourceManager, Recording recording ) { super( recording ); } @@ -64,7 +67,7 @@ public class RecordingNode extends BaseNode } else { - getModel().getResourceManager().getRecordingSourceManager() + mSourceManager.getRecordingSourceManager() .removeRecording( getRecording() ); getModel().removeNodeFromParent( RecordingNode.this ); diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index 0fcabaef..8c2f444f 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -31,21 +31,23 @@ import source.config.SourceConfigRecording; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; -import controller.ResourceManager; +import controller.ThreadPoolManager; public class RecordingSourceManager { private final static Logger mLog = LoggerFactory.getLogger( RecordingSourceManager.class ); - private ResourceManager mResourceManager; - private ArrayList mRecordings = new ArrayList(); - public RecordingSourceManager( ResourceManager resourceManager ) + private SettingsManager mSettingsManager; + private ThreadPoolManager mThreadPoolManager; + + public RecordingSourceManager( SettingsManager settingsManager, + ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; - + mSettingsManager = settingsManager; + mThreadPoolManager = threadPoolManager; loadRecordings(); } @@ -71,9 +73,7 @@ public class RecordingSourceManager if( recording != null ) { - retVal = recording.getChannel( - mResourceManager.getThreadPoolManager(), - tunerChannel ); + retVal = recording.getChannel( mThreadPoolManager, tunerChannel ); } else { @@ -116,7 +116,7 @@ public class RecordingSourceManager public Recording addRecording( RecordingConfiguration config ) { - Recording recording = new Recording( mResourceManager, config ); + Recording recording = new Recording( mSettingsManager, config ); mRecordings.add( recording ); @@ -126,7 +126,7 @@ public class RecordingSourceManager public void removeRecording( Recording recording ) { /* Remove the config from the settings manager */ - mResourceManager.getSettingsManager().removeRecordingConfiguration( + mSettingsManager.removeRecordingConfiguration( recording.getRecordingConfiguration() ); mRecordings.remove( recording ); @@ -134,24 +134,17 @@ public class RecordingSourceManager private void loadRecordings() { - if( mResourceManager != null ) - { - SettingsManager settingsManager = mResourceManager.getSettingsManager(); - - if( settingsManager != null ) - { - ArrayList recordingConfigurations = - settingsManager.getRecordingConfigurations(); + ArrayList recordingConfigurations = + mSettingsManager.getRecordingConfigurations(); - mLog.info( "discovered [" + recordingConfigurations.size() + "] recording configurations" ); - - for( RecordingConfiguration config: recordingConfigurations ) - { - Recording recording = new Recording( mResourceManager, config ); - - mRecordings.add( recording ); - } - } - } - } + mLog.info( "RecordingSourceManager - discovered [" + + recordingConfigurations.size() + "] recording configurations" ); + + for( RecordingConfiguration config: recordingConfigurations ) + { + Recording recording = new Recording( mSettingsManager, config ); + + mRecordings.add( recording ); + } + } } diff --git a/src/buffer/ShortComplexByteBuffer.java b/src/source/tuner/ITunerChannelProvider.java similarity index 54% rename from src/buffer/ShortComplexByteBuffer.java rename to src/source/tuner/ITunerChannelProvider.java index 2aff0558..06f7b2fa 100644 --- a/src/buffer/ShortComplexByteBuffer.java +++ b/src/source/tuner/ITunerChannelProvider.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -15,39 +15,31 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package buffer; +package source.tuner; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.Enumeration; +import java.util.concurrent.RejectedExecutionException; -import sample.complex.Complex; +import source.SourceException; +import controller.ThreadPoolManager; -public class ShortComplexByteBuffer implements Enumeration +public interface ITunerChannelProvider { - private ByteBuffer mByteBuffer; - - public ShortComplexByteBuffer( byte[] bytes, ByteOrder endianness ) - { - mByteBuffer = ByteBuffer.wrap( bytes ); - mByteBuffer.order( endianness ); - } - - public byte[] array() - { - return mByteBuffer.array(); - } + /** + * Returns a tuner frequency channel source, tuned to the correct frequency + * + * @param frequency - desired frequency + * + * @return - source for 48k sample rate + */ + public abstract TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, + TunerChannel channel ) throws RejectedExecutionException, SourceException; - @Override - public boolean hasMoreElements() - { - return mByteBuffer.hasRemaining(); - } - - @Override - public Complex nextElement() - { - return new Complex( (float)mByteBuffer.getShort(), - (float)mByteBuffer.getShort() ); - } + /** + * Releases the tuned channel resources + * + * @param channel - previously obtained tuner channel + */ + public abstract void releaseChannel( TunerChannelSource source ); + + } diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index e274c97f..805a3cb5 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -27,20 +27,18 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; +import settings.SettingsManager; import source.SourceException; -import source.tuner.frequency.FrequencyChangeBroadcaster; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; -import controller.ResourceManager; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.ThreadPoolManager; /** * Tuner - provides tuner channel sources, representing a channel frequency */ -public abstract class Tuner implements FrequencyChangeBroadcaster, - FrequencyChangeListener, - TunerChannelProvider +public abstract class Tuner implements IFrequencyChangeProcessor, + ITunerChannelProvider { private final static Logger mLog = LoggerFactory.getLogger( Tuner.class ); @@ -53,8 +51,8 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, protected CopyOnWriteArrayList> mSampleListeners = new CopyOnWriteArrayList>(); - protected CopyOnWriteArrayList - mFrequencyChangeListeners = new CopyOnWriteArrayList(); + protected CopyOnWriteArrayList + mFrequencyChangeProcessors = new CopyOnWriteArrayList(); public Tuner( String name ) { @@ -69,7 +67,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, public void dispose() { mSampleListeners.clear(); - mFrequencyChangeListeners.clear(); + mFrequencyChangeProcessors.clear(); } public void setName( String name ) @@ -80,7 +78,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, /** * Return an editor panel for the tuner */ - public abstract JPanel getEditor( ResourceManager resourceManager ); + public abstract JPanel getEditor( SettingsManager settingsManager ); /** @@ -191,17 +189,17 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, * Adds the frequency change listener to receive frequency and/or bandwidth * change events */ - public void addListener( FrequencyChangeListener listener ) + public void addFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) { - mFrequencyChangeListeners.add( listener ); + mFrequencyChangeProcessors.add( processor ); } /** * Removes the frequency change listener */ - public void removeListener( FrequencyChangeListener listener ) + public void removeFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) { - mFrequencyChangeListeners.remove( listener ); + mFrequencyChangeProcessors.remove( processor ); } /** @@ -210,7 +208,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, public void broadcastFrequencyChange( long frequency ) { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.FREQUENCY_CHANGE_NOTIFICATION, frequency ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, frequency ) ); } /** @@ -219,7 +217,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, public void broadcastSampleRateChange( int sampleRate ) { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, sampleRate ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, sampleRate ) ); } /** @@ -228,7 +226,7 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, public void broadcastActualSampleRateChange( int actualSampleRate ) { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, actualSampleRate ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, actualSampleRate ) ); } /** @@ -236,9 +234,9 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, */ public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) { - for( FrequencyChangeListener listener: mFrequencyChangeListeners ) + for( IFrequencyChangeProcessor processor: mFrequencyChangeProcessors ) { - listener.frequencyChanged( event ); + processor.frequencyChanged( event ); } } diff --git a/src/source/tuner/TunerChannelProvider.java b/src/source/tuner/TunerChannelProvider.java deleted file mode 100644 index f8fabe48..00000000 --- a/src/source/tuner/TunerChannelProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -package source.tuner; - -import java.util.concurrent.RejectedExecutionException; - -import source.SourceException; -import controller.ThreadPoolManager; - -public interface TunerChannelProvider -{ - /** - * Returns a tuner frequency channel source, tuned to the correct frequency - * - * @param frequency - desired frequency - * - * @return - source for 48k sample rate - */ - public abstract TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, SourceException; - - /** - * Releases the tuned channel resources - * - * @param channel - previously obtained tuner channel - */ - public abstract void releaseChannel( TunerChannelSource source ); - - -} diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 0c61dc60..010a6f7b 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -36,7 +36,7 @@ import source.ComplexSource; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import dsp.filter.FilterFactory; @@ -45,8 +45,7 @@ import dsp.filter.cic.ComplexPrimeCICDecimate; import dsp.mixer.Oscillator; public class TunerChannelSource extends ComplexSource - implements FrequencyChangeListener, - Listener + implements IFrequencyChangeProcessor, Listener { private final static Logger mLog = LoggerFactory.getLogger( TunerChannelSource.class ); @@ -61,7 +60,7 @@ public class TunerChannelSource extends ComplexSource private Oscillator mMixer; private ComplexPrimeCICDecimate mDecimationFilter; private Listener mListener; - private FrequencyChangeListener mFrequencyChangeListener; + private IFrequencyChangeProcessor mFrequencyChangeProcessor; private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mTaskHandle; @@ -71,17 +70,35 @@ public class TunerChannelSource extends ComplexSource private DecimationProcessor mDecimationProcessor = new DecimationProcessor(); - public TunerChannelSource( ThreadPoolManager threadPoolManager, - Tuner tuner, + private AtomicBoolean mRunning = new AtomicBoolean(); + private boolean mExpended = false; + + /** + * Provides a Digital Drop Channel (DDC) to decimate the IQ output from a + * tuner down to a 48 kHz IQ channel rate. + * + * Note: this class can only be used once (started and stopped) and a new + * tuner channel source must be requested from the tuner once this object + * has been stopped. This is because channels are managed dynamically and + * center tuned frequency may have changed since this source was obtained + * and thus the tuner might no longer be able to source this channel once it + * has been stopped. + * + * @param threadPoolManager to use for decimation processing task + * @param tuner to obtain wideband IQ samples from + * @param tunerChannel specifying the center frequency for the DDC + * @throws RejectedExecutionException if the thread pool manager cannot + * accept the decimation processing task + * @throws SourceException if the tuner has an issue providing IQ samples + */ + public TunerChannelSource( ThreadPoolManager threadPoolManager, Tuner tuner, TunerChannel tunerChannel ) throws RejectedExecutionException, SourceException { - super( "Tuner Channel Source" ); - mThreadPoolManager = threadPoolManager; mTuner = tuner; - mTuner.addListener( (FrequencyChangeListener)this ); + mTuner.addListener( this ); mTunerChannel = tunerChannel; @@ -94,59 +111,87 @@ public class TunerChannelSource extends ComplexSource /* Fire a sample rate change event to setup the decimation chain */ frequencyChanged( new FrequencyChangeEvent( - Event.SAMPLE_RATE_CHANGE_NOTIFICATION, mTuner.getSampleRate() ) ); - - /* Schedule the decimation task to run every 20 ms (50 iterations/second) */ - mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION, - mDecimationProcessor, 20, TimeUnit.MILLISECONDS ); - - /* Finally, register to receive samples from the tuner */ - mTuner.addListener( (Listener)this ); + Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getSampleRate() ) ); } + @Override + public void reset() + { + } + + + @Override + public void start() + { + if( mExpended ) + { + throw new IllegalStateException( "Attempt to re-start an expended " + + "tuner channel source. TunerChannelSource objects can only " + + "be used once. " ); + } + + if( mRunning.compareAndSet( false, true )) + { + /* Schedule the decimation task to run every 20 ms (50 iterations/second) */ + mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION, + mDecimationProcessor, 20, TimeUnit.MILLISECONDS ); + + /* Finally, register to receive samples from the tuner */ + mTuner.addListener( (Listener)this ); + } + else + { + mLog.warn( "Attempt to start() and already running tuner channel " + + "source was ignored" ); + } + } + + + @Override + public void stop() + { + if( mRunning.compareAndSet( true, false )) + { + mTuner.releaseChannel( this ); + mDecimationProcessor.shutdown(); + + if( mThreadPoolManager != null && mTaskHandle != null ) + { + mThreadPoolManager.cancel( mTaskHandle ); + mTaskHandle = null; + } + + mBuffer.clear(); + + mExpended = true; + } + else + { + mLog.warn( "Attempt to stop() and already stopped tuner channel " + + "source was ignored" ); + } + } + + + @Override public void dispose() { - /* Tell the tuner to release/unregister our resources */ - mTuner.removeListener( (FrequencyChangeListener)this ); - mTuner.releaseChannel( this ); - mTuner = null; - mTunerChannel = null; - - /* Flag the decimation processor to shutdown */ - if( mDecimationProcessor != null ) - { - mDecimationProcessor.dispose(); - } - + if( !mRunning.get() ) + { + /* Tell the tuner to release/unregister our resources */ + mTuner.removeFrequencyChangeProcessor( this ); + mTuner = null; + mTunerChannel = null; + mBuffer = null; + mFrequencyChangeProcessor = null; + mListener = null; + mMixer = null; + mDecimationFilter.dispose(); + mDecimationFilter = null; + } } - /** - * Cleanup method to allow the decimation processor to perform final cleanup - * once it gracefully shuts itself down - */ - private void cleanup() - { - if( mTaskHandle != null ) - { - mThreadPoolManager.cancel( mTaskHandle ); - mThreadPoolManager = null; - mTaskHandle = null; - } - - - mBuffer.clear(); - mBuffer = null; - - mFrequencyChangeListener = null; - mListener = null; - - mMixer = null; - - mDecimationFilter.dispose(); - mDecimationFilter = null; - } - public Tuner getTuner() { return mTuner; @@ -160,18 +205,21 @@ public class TunerChannelSource extends ComplexSource @Override public void receive( ComplexBuffer buffer ) { - mBuffer.add( buffer ); + if( mRunning.get() ) + { + mBuffer.add( buffer ); + } } - public void setFrequencyChangeListener( FrequencyChangeListener listener ) + public void setFrequencyChangeListener( IFrequencyChangeProcessor processor ) { - mFrequencyChangeListener = listener; + mFrequencyChangeProcessor = processor; } @Override public void setListener( Listener listener ) { - /* Get a pointer to the listener so that if we have to change the + /* Save a pointer to the listener so that if we have to change the * decimation filter, we can re-add the listener */ mListener = listener; @@ -192,31 +240,31 @@ public class TunerChannelSource extends ComplexSource public void frequencyChanged( FrequencyChangeEvent event ) { // Echo the event to the registered event listener - if( mFrequencyChangeListener != null ) + if( mFrequencyChangeProcessor != null ) { - mFrequencyChangeListener.frequencyChanged( event ); + mFrequencyChangeProcessor.frequencyChanged( event ); } switch( event.getEvent() ) { - case FREQUENCY_CHANGE_NOTIFICATION: + case NOTIFICATION_FREQUENCY_CHANGE: mTunerFrequency = event.getValue().longValue(); updateMixerFrequencyOffset(); break; - case CHANNEL_FREQUENCY_CORRECTION_CHANGE_REQUEST: + case REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE: mChannelFrequencyCorrection = event.getValue().intValue(); updateMixerFrequencyOffset(); - if( mFrequencyChangeListener != null ) + if( mFrequencyChangeProcessor != null ) { - mFrequencyChangeListener.frequencyChanged( + mFrequencyChangeProcessor.frequencyChanged( new FrequencyChangeEvent( - Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, + Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, mChannelFrequencyCorrection ) ); } break; - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: int sampleRate = event.getValue().intValue(); if( mTunerSampleRate != sampleRate ) @@ -272,12 +320,12 @@ public class TunerChannelSource extends ComplexSource */ public class DecimationProcessor implements Runnable { - private AtomicBoolean mDispose = new AtomicBoolean( false ); + private boolean mProcessing = true; private List mSampleBuffers = new ArrayList(); - public void dispose() + public void shutdown() { - mDispose.set( true ); + mProcessing = false; } @Override @@ -288,64 +336,66 @@ public class TunerChannelSource extends ComplexSource * run the program out of memory */ try { - if( mBuffer != null ) + if( mProcessing ) { - mBuffer.drainTo( mSampleBuffers, 4 ); - - for( Buffer buffer: mSampleBuffers ) + if( mBuffer != null ) { - /* Check to see if we've been shutdown */ - if( mDispose.get() ) + mBuffer.drainTo( mSampleBuffers, 4 ); + + for( Buffer buffer: mSampleBuffers ) { - cleanup(); - return; - } - else - { - float[] samples = buffer.getSamples(); - - /* We make a copy of the buffer so that we don't affect - * anyone else that is using the same buffer, like other - * channels or the spectral display */ - float[] translated = new float[ samples.length ]; - - /* Perform frequency translation */ - for( int x = 0; x < samples.length; x += 2 ) + /* Check to see if we've been shutdown */ + if( !mProcessing ) { - mMixer.rotate(); + mBuffer.clear(); + return; + } + else + { + float[] samples = buffer.getSamples(); + + /* We make a copy of the buffer so that we don't affect + * anyone else that is using the same buffer, like other + * channels or the spectral display */ + float[] translated = new float[ samples.length ]; - translated[ x ] = Complex.multiplyInphase( - samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); - - translated[ x + 1 ] = Complex.multiplyQuadrature( + /* Perform frequency translation */ + for( int x = 0; x < samples.length; x += 2 ) + { + mMixer.rotate(); + + translated[ x ] = Complex.multiplyInphase( samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); - } - - if( mDecimationFilter != null ) - { - mDecimationFilter.receive( new ComplexBuffer( translated ) ); + + translated[ x + 1 ] = Complex.multiplyQuadrature( + samples[ x ], samples[ x + 1 ], mMixer.inphase(), mMixer.quadrature() ); + } + + if( mProcessing && mDecimationFilter != null ) + { + mDecimationFilter.receive( new ComplexBuffer( translated ) ); + } } } + + mSampleBuffers.clear(); } - - mSampleBuffers.clear(); } } catch( Exception e ) { - /* Only log the stack trace if we've haven't been shutdown and - * this is a true error */ - if( !mDispose.get() ) + /* Only log the stack trace if we're still processing */ + if( mProcessing ) { mLog.error( "Error encountered during decimation process", e ); } } /* Check to see if we've been shutdown */ - if( mDispose.get() ) + if( !mProcessing ) { + mBuffer.clear(); mSampleBuffers.clear(); - cleanup(); } } } diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index aacb5f6b..44682e7a 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -25,9 +25,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyController; import source.tuner.frequency.FrequencyController.Tunable; +import source.tuner.frequency.IFrequencyChangeProcessor; +import source.tuner.frequency.IFrequencyChangeListener; import controller.ThreadPoolManager; public abstract class TunerController implements Tunable @@ -36,7 +37,11 @@ public abstract class TunerController implements Tunable LoggerFactory.getLogger( TunerController.class ); /* List of currently tuned channels being served to demod channels */ +<<<<<<< Upstream, based on origin/master private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); +======= + protected ArrayList mTunedChannels = new ArrayList<>(); +>>>>>>> 20e3bee Work in progress. -Started work on new channel editor channel viewer/controller -Removed a number of unused buffer package classes -Moved automatic frequency control to be a module and removed special handling -Added IUnfilteredRealBufferListener & Provider interfaces -Completely removed the ResourceManager class protected FrequencyController mFrequencyController; private int mMiddleUnusable; private double mUsableBandwidthPercentage; @@ -359,16 +364,16 @@ public abstract class TunerController implements Tunable * Note: this is normally used by the Tuner. Any additional listeners can * be registered on the tuner. */ - public void addListener( FrequencyChangeListener listener ) + public void addListener( IFrequencyChangeProcessor processor ) { - mFrequencyController.addListener( listener ); + mFrequencyController.addListener( processor ); } /** * Removes the frequency change listener */ - public void removeListener( FrequencyChangeListener listener ) + public void removeListener( IFrequencyChangeProcessor processor ) { - mFrequencyController.removeListener( listener ); + mFrequencyController.removeFrequencyChangeProcessor( processor ); } } diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 15b3d650..2be792a1 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -17,21 +17,23 @@ ******************************************************************************/ package source.tuner; +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; import gui.control.JFrequencyControl; import source.SourceEditor; +import source.SourceManager; import source.config.SourceConfigTuner; import source.config.SourceConfiguration; -import controller.ResourceManager; public class TunerEditor extends SourceEditor { private static final long serialVersionUID = 1L; private JFrequencyControl mFrequencyControl; - public TunerEditor( ResourceManager resourceManager, + public TunerEditor( SourceManager sourceManager, SourceConfiguration config ) { - super( resourceManager, config ); + super( sourceManager, config ); initGUI(); } @@ -56,4 +58,18 @@ public class TunerEditor extends SourceEditor add( mFrequencyControl ); } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } } diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index 345757f3..3d6f351c 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -30,6 +30,7 @@ import org.usb4java.DeviceDescriptor; import org.usb4java.DeviceList; import org.usb4java.LibUsb; +import settings.SettingsManager; import source.Source; import source.SourceException; import source.config.SourceConfigTuner; @@ -46,22 +47,25 @@ import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; import source.tuner.rtl.r820t.R820TTunerController; -import controller.ResourceManager; +import controller.ThreadPoolManager; public class TunerManager { private final static Logger mLog = LoggerFactory.getLogger( TunerManager.class ); - private ResourceManager mResourceManager; private ArrayList mTuners = new ArrayList(); + + private MixerManager mMixerManager; + private SettingsManager mSettingsManager; + private ThreadPoolManager mThreadPoolManager; - public TunerManager( ResourceManager resourceManager ) + public TunerManager( MixerManager mixerManager, + SettingsManager settingsManager, + ThreadPoolManager threadPoolManager ) { - mResourceManager = resourceManager; - - /* initialize mixers */ - MixerManager.getInstance(); + mSettingsManager = settingsManager; + mThreadPoolManager = threadPoolManager; initTuners(); } @@ -100,8 +104,7 @@ public class TunerManager try { - retVal = tuner.getChannel( - mResourceManager.getThreadPoolManager(), tunerChannel ); + retVal = tuner.getChannel( mThreadPoolManager, tunerChannel ); } catch ( RejectedExecutionException ree ) { @@ -261,7 +264,7 @@ public class TunerManager try { AirspyTunerController airspyController = - new AirspyTunerController( device, mResourceManager.getThreadPoolManager() ); + new AirspyTunerController( device, mThreadPoolManager ); airspyController.init(); @@ -397,7 +400,7 @@ public class TunerManager { HackRFTunerController hackRFController = new HackRFTunerController( device, descriptor, - mResourceManager.getThreadPoolManager() ); + mThreadPoolManager ); hackRFController.init(); @@ -449,7 +452,7 @@ public class TunerManager { E4KTunerController controller = new E4KTunerController( device, deviceDescriptor, - mResourceManager.getThreadPoolManager() ); + mThreadPoolManager ); controller.init(); @@ -476,7 +479,7 @@ public class TunerManager { R820TTunerController controller = new R820TTunerController( device, deviceDescriptor, - mResourceManager.getThreadPoolManager() ); + mThreadPoolManager ); controller.init(); @@ -526,12 +529,11 @@ public class TunerManager */ private TunerConfiguration getTunerConfiguration( Tuner tuner ) { - TunerConfigurationAssignment selected = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( - tuner.getTunerType(), tuner.getUniqueID() ); + TunerConfigurationAssignment selected = mSettingsManager + .getSelectedTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - ArrayList configs = mResourceManager - .getSettingsManager().getTunerConfigurations( tuner.getTunerType() ); + ArrayList configs = mSettingsManager + .getTunerConfigurations( tuner.getTunerType() ); TunerConfiguration config = null; @@ -553,13 +555,13 @@ public class TunerManager //If we're still null at this point, create a default config if( config == null ) { - config = mResourceManager.getSettingsManager() - .addNewTunerConfiguration( tuner.getTunerType(), "Default" ); + config = mSettingsManager.addNewTunerConfiguration( + tuner.getTunerType(), "Default" ); } //Persist the config as the selected tuner configuration. The method //auto-saves the setting - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID(), config ); return config; @@ -579,7 +581,7 @@ public class TunerManager private MixerTunerDataLine getMixerTunerDataLine( TunerType tunerClass ) { Collection datalines = - MixerManager.getInstance().getMixerTunerDataLines(); + mMixerManager.getMixerTunerDataLines(); for( MixerTunerDataLine mixerTDL: datalines ) { diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java index 626cda6b..63aabd9c 100644 --- a/src/source/tuner/TunerNode.java +++ b/src/source/tuner/TunerNode.java @@ -28,15 +28,17 @@ import javax.swing.JPopupMenu; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.PlaylistManager; +import settings.SettingsManager; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import spectrum.SpectrumFrame; import controller.BaseNode; -import controller.ResourceManager; import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; -public class TunerNode extends BaseNode implements FrequencyChangeListener +public class TunerNode extends BaseNode implements IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( TunerNode.class ); @@ -48,14 +50,24 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener private long mFrequency; private int mSampleRate; private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; - public TunerNode( ChannelModel channelModel, Tuner tuner ) + public TunerNode( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SettingsManager settingsManager, + Tuner tuner ) { super( tuner ); //does this make it a super tuner? mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; - tuner.addListener( this ); + tuner.addFrequencyChangeProcessor( this ); try { @@ -72,7 +84,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener @Override public JPanel getEditor() { - return getTuner().getEditor( getModel().getResourceManager() ); + return getTuner().getEditor( mSettingsManager ); } public Tuner getTuner() @@ -117,11 +129,9 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener @Override public void actionPerformed( ActionEvent e ) { - ResourceManager rm = getModel().getResourceManager(); - - SpectrumFrame frame = new SpectrumFrame( mChannelModel, - rm.getController(), rm.getPlaylistManager(), - rm.getSettingsManager(), getTuner() ); + SpectrumFrame frame = new SpectrumFrame( getModel(), mChannelModel, + mChannelProcessingManager, mPlaylistManager, + mSettingsManager, getTuner() ); } } ); @@ -135,7 +145,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener { switch( event.getEvent() ) { - case FREQUENCY_CHANGE_NOTIFICATION: + case NOTIFICATION_FREQUENCY_CHANGE: long frequency = event.getValue().longValue(); if( mFrequency != frequency ) @@ -144,7 +154,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener getModel().nodeChanged( TunerNode.this ); } break; - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: int sampleRate = event.getValue().intValue(); if( mSampleRate != sampleRate ) diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 36df0315..345b3456 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; +import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; import source.tuner.TunerChannel; @@ -34,7 +35,6 @@ import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.BoardID; -import controller.ResourceManager; import controller.ThreadPoolManager; public class AirspyTuner extends Tuner @@ -55,9 +55,9 @@ public class AirspyTuner extends Tuner } @Override - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { - return new AirspyTunerEditorPanel( this, resourceManager ); + return new AirspyTunerEditorPanel( this, settingsManager ); } public AirspyTunerController getController() diff --git a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java index e2d31475..937a63f8 100644 --- a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java +++ b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java @@ -45,13 +45,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.Gain; import source.tuner.airspy.AirspyTunerController.GainMode; -import controller.ResourceManager; public class AirspyTunerConfigurationPanel extends JPanel { @@ -60,7 +60,7 @@ public class AirspyTunerConfigurationPanel extends JPanel private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private AirspyTunerController mController; private AirspyTunerConfiguration mSelectedConfig; @@ -94,10 +94,10 @@ public class AirspyTunerConfigurationPanel extends JPanel private JSlider mMixerGain; - public AirspyTunerConfigurationPanel( ResourceManager resourceManager, + public AirspyTunerConfigurationPanel( SettingsManager settingsManager, AirspyTunerController controller ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mController = controller; init(); @@ -126,8 +126,7 @@ public class AirspyTunerConfigurationPanel extends JPanel if( serial != null ) { - savedConfig = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( + savedConfig = mSettingsManager.getSelectedTunerConfiguration( TunerType.AIRSPY_R820T, serial ); } @@ -146,8 +145,7 @@ public class AirspyTunerConfigurationPanel extends JPanel if( serial != null ) { - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( TunerType.AIRSPY_R820T, + mSettingsManager.setSelectedTunerConfiguration( TunerType.AIRSPY_R820T, serial, mSelectedConfig ); } } @@ -518,8 +516,7 @@ public class AirspyTunerConfigurationPanel extends JPanel public void actionPerformed( ActionEvent e ) { TunerConfiguration config = - mResourceManager.getSettingsManager() - .addNewTunerConfiguration( + mSettingsManager.addNewTunerConfiguration( TunerType.AIRSPY_R820T, "New Configuration" ); @@ -558,8 +555,7 @@ public class AirspyTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mResourceManager.getSettingsManager() - .deleteTunerConfiguration( selected ); + mSettingsManager.deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -635,7 +631,7 @@ public class AirspyTunerConfigurationPanel extends JPanel mGainModeCombo.setSelectedItem( Gain.getGainMode( gain ) ); mGain.setValue( gain.getValue() ); - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( TunerType.AIRSPY_R820T, mController.getDeviceInfo().getSerialNumber(), config ); save(); @@ -661,8 +657,7 @@ public class AirspyTunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.AIRSPY_R820T ); + mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); DefaultComboBoxModel model = new DefaultComboBoxModel(); @@ -681,8 +676,7 @@ public class AirspyTunerConfigurationPanel extends JPanel private AirspyTunerConfiguration getNamedConfiguration( String name ) { ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.AIRSPY_R820T ); + mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); for( TunerConfiguration config: configs ) { @@ -700,6 +694,6 @@ public class AirspyTunerConfigurationPanel extends JPanel */ private void save() { - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } } diff --git a/src/source/tuner/airspy/AirspyTunerEditorPanel.java b/src/source/tuner/airspy/AirspyTunerEditorPanel.java index d15037f7..7305fce4 100644 --- a/src/source/tuner/airspy/AirspyTunerEditorPanel.java +++ b/src/source/tuner/airspy/AirspyTunerEditorPanel.java @@ -29,17 +29,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - public class AirspyTunerEditorPanel extends JPanel - implements FrequencyChangeListener + implements IFrequencyChangeProcessor { private static final long serialVersionUID = 1L; @@ -50,14 +49,14 @@ public class AirspyTunerEditorPanel extends JPanel private AirspyTuner mTuner; private AirspyTunerController mController; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; public AirspyTunerEditorPanel( AirspyTuner tuner, - ResourceManager resourceManager ) + SettingsManager settingsManager ) { mTuner = tuner; mController = mTuner.getController(); - mResourceManager = resourceManager; + mSettingsManager = settingsManager; initGUI(); } @@ -76,7 +75,7 @@ public class AirspyTunerEditorPanel extends JPanel /* Add frequency control as frequency change listener. This creates a * feedback loop, so the control does not rebroadcast the event */ - mTuner.addListener( mFrequencyControl ); + mTuner.addFrequencyChangeProcessor( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); @@ -89,7 +88,7 @@ public class AirspyTunerEditorPanel extends JPanel add( tabs, "span,grow,push" ); tabs.add( "Config", new AirspyTunerConfigurationPanel( - mResourceManager, mTuner.getController() ) ); + mSettingsManager, mTuner.getController() ) ); tabs.add( "Info", new AirspyInformationPanel( mTuner.getController() ) ); } @@ -97,7 +96,7 @@ public class AirspyTunerEditorPanel extends JPanel @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { try { diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java index 9ec27054..11cbc94f 100644 --- a/src/source/tuner/ettus/B100Tuner.java +++ b/src/source/tuner/ettus/B100Tuner.java @@ -24,6 +24,7 @@ import javax.swing.JPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; import source.tuner.TunerChannel; @@ -32,7 +33,6 @@ import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.usb.USBTunerDevice; -import controller.ResourceManager; import controller.ThreadPoolManager; public class B100Tuner extends Tuner @@ -69,9 +69,9 @@ public class B100Tuner extends Tuner } @Override - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { - return new B100TunerEditorPanel( this, resourceManager ); + return new B100TunerEditorPanel( this, settingsManager ); } @Override diff --git a/src/source/tuner/ettus/B100TunerEditorPanel.java b/src/source/tuner/ettus/B100TunerEditorPanel.java index 964ac4ee..aadd2fec 100644 --- a/src/source/tuner/ettus/B100TunerEditorPanel.java +++ b/src/source/tuner/ettus/B100TunerEditorPanel.java @@ -22,19 +22,19 @@ import javax.swing.JLabel; import javax.swing.JPanel; import net.miginfocom.swing.MigLayout; -import controller.ResourceManager; +import settings.SettingsManager; public class B100TunerEditorPanel extends JPanel { private static final long serialVersionUID = 1L; private B100Tuner mTuner; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; - public B100TunerEditorPanel( B100Tuner tuner, ResourceManager resourceManager ) + public B100TunerEditorPanel( B100Tuner tuner, SettingsManager settingsManager ) { mTuner = tuner; - mResourceManager = resourceManager; + mSettingsManager = settingsManager; init(); } diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index ea84063c..e3d680a5 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -24,6 +24,7 @@ import javax.swing.JPanel; import sample.Listener; import sample.adapter.ShortAdapter; import sample.complex.ComplexBuffer; +import settings.SettingsManager; import source.SourceException; import source.tuner.MixerTuner; import source.tuner.MixerTunerDataLine; @@ -32,8 +33,6 @@ import source.tuner.TunerChannelSource; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; -import source.tuner.frequency.FrequencyChangeListener; -import controller.ResourceManager; import controller.ThreadPoolManager; public class FCDTuner extends MixerTuner @@ -48,7 +47,7 @@ public class FCDTuner extends MixerTuner new ShortAdapter() ); mController = controller; - mController.addListener( (FrequencyChangeListener)this ); + mController.addListener( this ); } public void dispose() @@ -82,9 +81,9 @@ public class FCDTuner extends MixerTuner } @Override - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { - return mController.getEditor( this, resourceManager ); + return mController.getEditor( this, settingsManager ); } @Override diff --git a/src/source/tuner/fcd/FCDTunerController.java b/src/source/tuner/fcd/FCDTunerController.java index 8e6ff030..ca602ed2 100644 --- a/src/source/tuner/fcd/FCDTunerController.java +++ b/src/source/tuner/fcd/FCDTunerController.java @@ -33,13 +33,13 @@ import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerController; import source.tuner.TunerType; import source.tuner.fcd.proV1.FCD1TunerController.Block; -import controller.ResourceManager; public abstract class FCDTunerController extends TunerController { @@ -150,7 +150,7 @@ public abstract class FCDTunerController extends TunerController * Editor panel (GUI) component. */ public abstract JPanel getEditor( FCDTuner tuner, - ResourceManager resourceManager ); + SettingsManager settingsManager ); /** * Applies the settings in the tuner configuration diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java index bfbe7fbb..b54bef0b 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java @@ -47,6 +47,7 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerType; @@ -56,15 +57,13 @@ import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - public class FCD1TunerConfigurationPanel extends JPanel { private final static Logger mLog = LoggerFactory.getLogger( FCD1TunerConfigurationPanel.class ); private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private FCD1TunerController mController; private FCD1TunerConfiguration mSelectedConfig; @@ -81,10 +80,10 @@ public class FCD1TunerConfigurationPanel extends JPanel private CorrectionSpinner mCorrectionGain; private CorrectionSpinner mCorrectionPhase; - public FCD1TunerConfigurationPanel( ResourceManager resourceManager, + public FCD1TunerConfigurationPanel( SettingsManager settingsManager, FCD1TunerController controller ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mController = controller; mSelectedConfig = controller.getTunerConfiguration(); @@ -428,8 +427,7 @@ public class FCD1TunerConfigurationPanel extends JPanel public void actionPerformed( ActionEvent e ) { TunerConfiguration config = - mResourceManager.getSettingsManager() - .addNewTunerConfiguration( + mSettingsManager.addNewTunerConfiguration( TunerType.FUNCUBE_DONGLE_PRO, "New Configuration" ); @@ -472,8 +470,7 @@ public class FCD1TunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mResourceManager.getSettingsManager() - .deleteTunerConfiguration( selected ); + mSettingsManager.deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -508,8 +505,7 @@ public class FCD1TunerConfigurationPanel extends JPanel mCorrectionGain.setValue( config.getGainCorrection() ); mCorrectionPhase.setValue( config.getPhaseCorrection() ); - mResourceManager.getSettingsManager() - .setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( TunerType.FUNCUBE_DONGLE_PRO, mController.getUSBAddress(), config ); } @@ -529,8 +525,7 @@ public class FCD1TunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); + mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); DefaultComboBoxModel model = new DefaultComboBoxModel(); @@ -547,8 +542,7 @@ public class FCD1TunerConfigurationPanel extends JPanel private FCD1TunerConfiguration getNamedConfiguration( String name ) { ArrayList configs = - mResourceManager.getSettingsManager() - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); + mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); for( TunerConfiguration config: configs ) { @@ -563,7 +557,7 @@ public class FCD1TunerConfigurationPanel extends JPanel private void save() { - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index 9d56b0f4..8de3053c 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -29,6 +29,7 @@ import org.slf4j.LoggerFactory; import org.usb4java.Device; import org.usb4java.DeviceDescriptor; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; @@ -36,7 +37,6 @@ import source.tuner.TunerType; import source.tuner.fcd.FCDCommand; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; -import controller.ResourceManager; public class FCD1TunerController extends FCDTunerController { @@ -148,11 +148,11 @@ public class FCD1TunerController extends FCDTunerController } @Override - public JPanel getEditor( FCDTuner tuner, ResourceManager resourceManager ) + public JPanel getEditor( FCDTuner tuner, SettingsManager settingsManager ) { if( mEditor == null ) { - mEditor = new FCD1TunerEditorPanel( tuner, resourceManager ); + mEditor = new FCD1TunerEditorPanel( tuner, settingsManager ); } return mEditor; diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java index 317fcaeb..83512616 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java @@ -29,18 +29,17 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerDetailsPanel; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - -public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListener +public class FCD1TunerEditorPanel extends JPanel implements IFrequencyChangeProcessor { private static final long serialVersionUID = 1L; private final static Logger mLog = @@ -48,15 +47,15 @@ public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListe private FCDTuner mTuner; private FCD1TunerController mController; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private JFrequencyControl mFrequencyControl; - public FCD1TunerEditorPanel( FCDTuner tuner, ResourceManager resourceManager ) + public FCD1TunerEditorPanel( FCDTuner tuner, SettingsManager settingsManager ) { mTuner = tuner; mController = (FCD1TunerController)mTuner.getController(); - mResourceManager = resourceManager; + mSettingsManager = settingsManager; initGUI(); } @@ -74,7 +73,7 @@ public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListe /* Add frequency control as listener to tuner to get frequency changes * that are invoked elsewhere, to keep the control in sync with the * frequency */ - mTuner.addListener( mFrequencyControl ); + mTuner.addFrequencyChangeProcessor( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); @@ -85,7 +84,7 @@ public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListe tabs.setForeground( Color.BLACK ); tabs.add( "Configuration", new FCD1TunerConfigurationPanel( - mResourceManager, mController ) ); + mSettingsManager, mController ) ); tabs.add( "Info", new FCDTunerDetailsPanel( mController ) ); @@ -95,7 +94,7 @@ public class FCD1TunerEditorPanel extends JPanel implements FrequencyChangeListe @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { try { diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java index bb6acbea..8bd20ac9 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java @@ -46,11 +46,11 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; -import controller.ResourceManager; public class FCD2TunerConfigurationPanel extends JPanel { @@ -58,7 +58,7 @@ public class FCD2TunerConfigurationPanel extends JPanel LoggerFactory.getLogger( FCD2TunerConfigurationPanel.class ); private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private FCD2TunerController mController; private FCD2TunerConfiguration mSelectedConfig; private JButton mNewConfiguration; @@ -69,10 +69,10 @@ public class FCD2TunerConfigurationPanel extends JPanel private JCheckBox mMixerGain; private JSpinner mFrequencyCorrection; - public FCD2TunerConfigurationPanel( ResourceManager resourceManager, + public FCD2TunerConfigurationPanel( SettingsManager settingsManager, FCD2TunerController controller ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mController = controller; init(); @@ -218,8 +218,8 @@ public class FCD2TunerConfigurationPanel extends JPanel * Lookup the save config and apply that config to update all of the * controls */ - TunerConfigurationAssignment savedConfig = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( + TunerConfigurationAssignment savedConfig = mSettingsManager + .getSelectedTunerConfiguration( TunerType.FUNCUBE_DONGLE_PRO_PLUS, mController.getUSBAddress() ); @@ -234,7 +234,7 @@ public class FCD2TunerConfigurationPanel extends JPanel mSelectedConfig = mComboConfigurations.getItemAt( 0 ); //Store this config as the default for this tuner at this address - mResourceManager.getSettingsManager() + mSettingsManager .setSelectedTunerConfiguration( TunerType.FUNCUBE_DONGLE_PRO_PLUS, mController.getUSBAddress(), mSelectedConfig ); @@ -253,7 +253,7 @@ public class FCD2TunerConfigurationPanel extends JPanel public void actionPerformed( ActionEvent e ) { TunerConfiguration config = - mResourceManager.getSettingsManager() + mSettingsManager .addNewTunerConfiguration( TunerType.FUNCUBE_DONGLE_PRO_PLUS, "New Configuration" ); @@ -293,7 +293,7 @@ public class FCD2TunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mResourceManager.getSettingsManager() + mSettingsManager .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -322,7 +322,7 @@ public class FCD2TunerConfigurationPanel extends JPanel mMixerGain.setSelected( config.getGainMixer() ); mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( TunerType.FUNCUBE_DONGLE_PRO_PLUS, mController.getUSBAddress(), config ); } @@ -342,7 +342,7 @@ public class FCD2TunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); DefaultComboBoxModel model = @@ -359,7 +359,7 @@ public class FCD2TunerConfigurationPanel extends JPanel private FCD2TunerConfiguration getNamedConfiguration( String name ) { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); for( TunerConfiguration config: configs ) @@ -375,6 +375,6 @@ public class FCD2TunerConfigurationPanel extends JPanel private void save() { - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } } diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java index 8640ea50..96a31b85 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -27,6 +27,7 @@ import org.slf4j.LoggerFactory; import org.usb4java.Device; import org.usb4java.DeviceDescriptor; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; @@ -34,7 +35,6 @@ import source.tuner.TunerType; import source.tuner.fcd.FCDCommand; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; -import controller.ResourceManager; public class FCD2TunerController extends FCDTunerController { @@ -89,11 +89,11 @@ public class FCD2TunerController extends FCDTunerController return TunerType.FUNCUBE_DONGLE_PRO_PLUS; } - public JPanel getEditor( FCDTuner tuner, ResourceManager resourceManager ) + public JPanel getEditor( FCDTuner tuner, SettingsManager settingsManager ) { if( mEditor == null ) { - mEditor = new FCD2TunerEditorPanel( tuner, resourceManager ); + mEditor = new FCD2TunerEditorPanel( tuner, settingsManager ); } return mEditor; diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java index e372a544..6f49b4dc 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java @@ -29,18 +29,17 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerDetailsPanel; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - -public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListener +public class FCD2TunerEditorPanel extends JPanel implements IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerEditorPanel.class ); @@ -48,15 +47,15 @@ public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListe private FCDTuner mTuner; private FCD2TunerController mController; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private JFrequencyControl mFrequencyControl; - public FCD2TunerEditorPanel( FCDTuner tuner, ResourceManager resourceManager ) + public FCD2TunerEditorPanel( FCDTuner tuner, SettingsManager settingsManager ) { mTuner = tuner; mController = (FCD2TunerController)mTuner.getController(); - mResourceManager = resourceManager; + mSettingsManager = settingsManager; initGUI(); } @@ -73,7 +72,7 @@ public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListe /* Add frequency control as frequency change listener. This creates a * feedback loop, so the control does not rebroadcast the event */ - mTuner.addListener( mFrequencyControl ); + mTuner.addFrequencyChangeProcessor( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); @@ -84,7 +83,7 @@ public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListe tabs.setForeground( Color.BLACK ); tabs.add( "Configuration", new FCD2TunerConfigurationPanel( - mResourceManager, mController ) ); + mSettingsManager, mController ) ); tabs.add( "Info", new FCDTunerDetailsPanel( mController ) ); @@ -94,7 +93,7 @@ public class FCD2TunerEditorPanel extends JPanel implements FrequencyChangeListe @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { //Disregard the bandwidth, which shouldn't be changing try diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl_R.java b/src/source/tuner/frequency/AutomaticFrequencyControl_R.java deleted file mode 100644 index 994ff0c6..00000000 --- a/src/source/tuner/frequency/AutomaticFrequencyControl_R.java +++ /dev/null @@ -1,181 +0,0 @@ -package source.tuner.frequency; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.real.RealSampleListener; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import buffer.FloatAveragingBuffer; - -public class AutomaticFrequencyControl_R extends FrequencyCorrectionControl - implements RealSampleListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( AutomaticFrequencyControl_R.class ); - - private static float LARGE_ERROR = 0.300f; - private static float MEDIUM_ERROR = 0.150f; - private static float SMALL_ERROR = 0.030f; - private static float FINE_ERROR = 0.015f; - - private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz - private static int MEDIUM_FREQUENCY_CORRECTION = 500; - private static int SMALL_FREQUENCY_CORRECTION = 100; - private static int FINE_FREQUENCY_CORRECTION = 50; - - private static int SAMPLE_THRESHOLD = 15000; - private Mode mMode = Mode.FAST; - - private FloatAveragingBuffer mBuffer = - new FloatAveragingBuffer( SAMPLE_THRESHOLD ); - - private int mSampleCounter = 0; - private int mSkipCounter = 0; - private int mSkipThreshold = 4; - private int mErrorCorrection = 0; - - public enum Mode - { - NORMAL,FAST; - } - - /** - * Automatic Frequency Control. Monitors DC bias present in an FM - * demodulated audio stream and issues automatic frequency corrections to - * maintain the DC bias close to zero. - * - * @param maximum - maximum allowable (+/-) frequency correction value. - */ - public AutomaticFrequencyControl_R( int maximum ) - { - super( maximum ); - } - - public void dispose() - { - super.dispose(); - - mBuffer = null; - } - - /** - * Resets the error correction value to zero and broadcasts a change event - */ - public void reset() - { - mErrorCorrection = 0; - mMode = Mode.FAST; - dispatch(); - } - - /** - * Broadcasts a frequency correction change event to the registered listener - */ - private void dispatch() - { - if( mListener != null ) - { - mListener.frequencyChanged( - new FrequencyChangeEvent( Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - mErrorCorrection ) ); - } - } - - @Override - public void receive( float sample ) - { - if( mMode == Mode.FAST ) - { - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - else - { - mSkipCounter++; - - if( mSkipCounter >= mSkipThreshold ) - { - mSkipCounter = 0; - - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - } - } - - private void update( float average ) - { - mMode = Mode.NORMAL; - - if( average > LARGE_ERROR ) - { - adjust( -LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > MEDIUM_ERROR ) - { - adjust( -MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > SMALL_ERROR ) - { - adjust( -SMALL_FREQUENCY_CORRECTION ); - } - else if( average > FINE_ERROR ) - { - adjust( -FINE_FREQUENCY_CORRECTION ); - } - else if( average < -LARGE_ERROR ) - { - adjust( LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -MEDIUM_ERROR ) - { - adjust( MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -SMALL_ERROR ) - { - adjust( SMALL_FREQUENCY_CORRECTION ); - } - else if( average < -FINE_ERROR ) - { - adjust( FINE_FREQUENCY_CORRECTION ); - } - } -} diff --git a/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java b/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java deleted file mode 100644 index 73970ec2..00000000 --- a/src/source/tuner/frequency/AutomaticFrequencyControl_RB.java +++ /dev/null @@ -1,197 +0,0 @@ -package source.tuner.frequency; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import sample.real.RealBuffer; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import buffer.FloatAveragingBuffer; - -public class AutomaticFrequencyControl_RB extends FrequencyCorrectionControl - implements Listener -{ - private final static Logger mLog = - LoggerFactory.getLogger( AutomaticFrequencyControl_RB.class ); - - private static float LARGE_ERROR = 0.300f; - private static float MEDIUM_ERROR = 0.150f; - private static float SMALL_ERROR = 0.030f; - private static float FINE_ERROR = 0.015f; - - private static int LARGE_FREQUENCY_CORRECTION = 1500; //Hertz - private static int MEDIUM_FREQUENCY_CORRECTION = 500; - private static int SMALL_FREQUENCY_CORRECTION = 100; - private static int FINE_FREQUENCY_CORRECTION = 50; - - private static int SAMPLE_THRESHOLD = 15000; - private Mode mMode = Mode.FAST; - - private FloatAveragingBuffer mBuffer = - new FloatAveragingBuffer( SAMPLE_THRESHOLD ); - - private int mSampleCounter = 0; - private int mSkipCounter = 0; - private int mSkipThreshold = 4; - private int mErrorCorrection = 0; - - public enum Mode - { - NORMAL,FAST; - } - - /** - * Automatic Frequency Control. Monitors DC bias present in an FM - * demodulated audio stream and issues automatic frequency corrections to - * maintain the DC bias close to zero. - * - * @param maximum - maximum allowable (+/-) frequency correction value. - */ - public AutomaticFrequencyControl_RB( int maximum ) - { - super( maximum ); - } - - public void dispose() - { - super.dispose(); - - mBuffer = null; - } - - /** - * Resets the error correction value to zero and broadcasts a change event - */ - public void reset() - { - super.reset(); - - mMode = Mode.FAST; - dispatch(); - } - - /** - * Broadcasts a frequency correction change event to the registered listener - */ - private void dispatch() - { - if( mListener != null ) - { - mListener.frequencyChanged( - new FrequencyChangeEvent( Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - mErrorCorrection ) ); - } - } - - @Override - public void receive( RealBuffer t ) - { - float average = 0.0f; - - for( float sample: t.getSamples() ) - { - average = mBuffer.get( sample ); - } - - //TODO: fix this - -// update( average ); - } - - public void receive( float sample ) - { - if( mMode == Mode.FAST ) - { - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - else - { - mSkipCounter++; - - if( mSkipCounter >= mSkipThreshold ) - { - mSkipCounter = 0; - - float average = mBuffer.get( sample ); - - mSampleCounter++; - - if( mSampleCounter >= SAMPLE_THRESHOLD ) - { - mSampleCounter = 0; - - update( average ); - } - } - } - } - - private void update( float average ) - { - mMode = Mode.NORMAL; - - if( average > LARGE_ERROR ) - { - adjust( -LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > MEDIUM_ERROR ) - { - adjust( -MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average > SMALL_ERROR ) - { - adjust( -SMALL_FREQUENCY_CORRECTION ); - } - else if( average > FINE_ERROR ) - { - adjust( -FINE_FREQUENCY_CORRECTION ); - } - else if( average < -LARGE_ERROR ) - { - adjust( LARGE_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -MEDIUM_ERROR ) - { - adjust( MEDIUM_FREQUENCY_CORRECTION ); - mMode = Mode.FAST; - } - else if( average < -SMALL_ERROR ) - { - adjust( SMALL_FREQUENCY_CORRECTION ); - } - else if( average < -FINE_ERROR ) - { - adjust( FINE_FREQUENCY_CORRECTION ); - } - } -} diff --git a/src/source/tuner/frequency/DCTrackingFrequencyControl.java b/src/source/tuner/frequency/DCTrackingFrequencyControl.java new file mode 100644 index 00000000..17704dfd --- /dev/null +++ b/src/source/tuner/frequency/DCTrackingFrequencyControl.java @@ -0,0 +1,135 @@ +package source.tuner.frequency; + +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2016 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 + ******************************************************************************/ + +import module.Module; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import sample.real.RealBuffer; +import source.tuner.frequency.FrequencyChangeEvent.Event; + +public class DCTrackingFrequencyControl extends Module + implements Listener, IFrequencyChangeProvider +{ + private final static Logger mLog = + LoggerFactory.getLogger( DCTrackingFrequencyControl.class ); + + private static float COARSE_THRESHOLD = 0.1f; + private static float LOOP_GAIN = 0.1f; + private static float COARSE_GAIN = 1000.0f; + private static float FINE_GAIN = 100.0f; + private int mErrorCorrection = 0; + private int mMaximumFrequencyCorrection = 0; + private Listener mListener; + + /** + * DC offset Automatic Frequency Control. Monitors DC bias present in an FM + * demodulated audio stream and issues automatic frequency corrections to + * maintain the DC bias close to zero. + * + * @param maximum - maximum allowable (+/-) frequency correction value. + */ + public DCTrackingFrequencyControl( int maximum ) + { + mMaximumFrequencyCorrection = maximum; + } + + public void dispose() + { + mListener = null; + } + + @Override + public void receive( RealBuffer buffer ) + { + double sum = 0.0d; + + for( float sample: buffer.getSamples() ) + { + sum += sample; + } + + float mean = (float)( sum / (double)buffer.getSamples().length ); + + float adjustment = 0.0f; + + if( mean > COARSE_THRESHOLD || mean < - COARSE_THRESHOLD ) + { + adjustment = mean * COARSE_GAIN; + } + else + { + adjustment = mean * FINE_GAIN; + } + + setErrorCorrection( mErrorCorrection + (int)( (float)adjustment * LOOP_GAIN ) ); + } + + private void setErrorCorrection( int correction ) + { + if( mListener != null ) + { + if( correction > mMaximumFrequencyCorrection ) + { + correction = mMaximumFrequencyCorrection; + } + else if( correction < -mMaximumFrequencyCorrection ) + { + correction = -mMaximumFrequencyCorrection; + } + + if( correction != mErrorCorrection ) + { + mListener.receive( new FrequencyChangeEvent( + Event.REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE, correction ) ); + } + } + } + + @Override + public void setFrequencyChangeListener( Listener listener ) + { + mListener = listener; + } + + @Override + public void removeFrequencyChangeListener() + { + mListener = null; + } + + @Override + public void start() + { + } + + @Override + public void stop() + { + } + + @Override + public void reset() + { + setErrorCorrection( 0 ); + } +} diff --git a/src/source/tuner/frequency/FrequencyChangeBroadcaster.java b/src/source/tuner/frequency/FrequencyChangeBroadcaster.java deleted file mode 100644 index 89c03bf6..00000000 --- a/src/source/tuner/frequency/FrequencyChangeBroadcaster.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.frequency; - - -public interface FrequencyChangeBroadcaster -{ - public abstract void addListener( FrequencyChangeListener listener ); - - public abstract void removeListener( FrequencyChangeListener listener ); -} diff --git a/src/source/tuner/frequency/FrequencyChangeEvent.java b/src/source/tuner/frequency/FrequencyChangeEvent.java index 87f82f08..afadd058 100644 --- a/src/source/tuner/frequency/FrequencyChangeEvent.java +++ b/src/source/tuner/frequency/FrequencyChangeEvent.java @@ -4,11 +4,12 @@ public class FrequencyChangeEvent { public enum Event { - FREQUENCY_CHANGE_NOTIFICATION, - FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - CHANNEL_FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, - CHANNEL_FREQUENCY_CORRECTION_CHANGE_REQUEST, - SAMPLE_RATE_CHANGE_NOTIFICATION; + NOTIFICATION_FREQUENCY_CHANGE, + NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, + NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, + NOTIFICATION_SAMPLE_RATE_CHANGE, + + REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE; } private Event mEvent; diff --git a/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java b/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java new file mode 100644 index 00000000..1b96ae8b --- /dev/null +++ b/src/source/tuner/frequency/FrequencyChangeProcessorWrapper.java @@ -0,0 +1,32 @@ +package source.tuner.frequency; + +import sample.Listener; + +public class FrequencyChangeProcessorWrapper + implements IFrequencyChangeListener, Listener +{ + private IFrequencyChangeProcessor mProcessor; + + /** + * Provides IFrequencyChangeListener wrapper for an IFrequencyChangeProcessor + */ + public FrequencyChangeProcessorWrapper( IFrequencyChangeProcessor handler ) + { + mProcessor = handler; + } + + @Override + public Listener getFrequencyChangeListener() + { + return this; + } + + @Override + public void receive( FrequencyChangeEvent event ) + { + if( mProcessor != null ) + { + mProcessor.frequencyChanged( event ); + } + } +} diff --git a/src/source/tuner/frequency/FrequencyController.java b/src/source/tuner/frequency/FrequencyController.java index 51bab16f..f1c6753d 100644 --- a/src/source/tuner/frequency/FrequencyController.java +++ b/src/source/tuner/frequency/FrequencyController.java @@ -37,8 +37,7 @@ public class FrequencyController private double mFrequencyCorrection = 0d; private int mSampleRate = 0; - private ArrayList mListeners = - new ArrayList(); + private ArrayList mProcessors = new ArrayList<>(); public FrequencyController( Tunable tunable, long minFrequency, @@ -181,20 +180,21 @@ public class FrequencyController /** * Adds listener to receive frequency change events */ - public void addListener( FrequencyChangeListener listener ) +//TODO: rename this to addFrequencyChangeProcessor() + public void addListener( IFrequencyChangeProcessor processor ) { - if( !mListeners.contains( listener ) ) + if( !mProcessors.contains( processor ) ) { - mListeners.add( listener ); + mProcessors.add( processor ); } } /** * Removes listener from receiving frequency change events */ - public void removeListener( FrequencyChangeListener listener ) + public void removeFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) { - mListeners.remove( listener ); + mProcessors.remove( processor ); } @@ -205,7 +205,7 @@ public class FrequencyController protected void broadcastFrequencyChange() { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.FREQUENCY_CHANGE_NOTIFICATION, mFrequency ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, mFrequency ) ); } /** @@ -214,7 +214,7 @@ public class FrequencyController protected void broadcastFrequencyErrorChange() { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.FREQUENCY_CORRECTION_CHANGE_NOTIFICATION, mFrequencyCorrection ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, mFrequencyCorrection ) ); } /** @@ -223,14 +223,14 @@ public class FrequencyController protected void broadcastSampleRateChange() { broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, mSampleRate ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mSampleRate ) ); } public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) { - for( FrequencyChangeListener listener: mListeners ) + for( IFrequencyChangeProcessor processor: mProcessors ) { - listener.frequencyChanged( event ); + processor.frequencyChanged( event ); } } diff --git a/src/source/tuner/frequency/FrequencyCorrectionControl.java b/src/source/tuner/frequency/FrequencyCorrectionControl.java deleted file mode 100644 index 77255bb0..00000000 --- a/src/source/tuner/frequency/FrequencyCorrectionControl.java +++ /dev/null @@ -1,161 +0,0 @@ -package source.tuner.frequency; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.tuner.frequency.FrequencyChangeEvent.Event; - -/** - * Frequency control for providing frequency error correction to a tuned channel - */ -public class FrequencyCorrectionControl implements FrequencyChangeListener -{ - private final static Logger mLog = - LoggerFactory.getLogger( FrequencyCorrectionControl.class ); - - protected FrequencyChangeListener mListener; - protected FrequencyCorrectionResetListener mResetListener; - - protected int mChannelFrequencyCorrection = 0; - private int mMaximumCorrection = 3000; - - /** - * Frequency correction controller. - * - * @param maximum - defines the maximum +/- allowable frequency correction - * value - */ - public FrequencyCorrectionControl( int maximum ) - { - mMaximumCorrection = maximum; - } - - /** - * Cleanup - */ - public void dispose() - { - mListener = null; - mResetListener = null; - } - - /** - * Registers a listener to receive frequency correction events - */ - public void setFrequencyChangeListener( FrequencyChangeListener listener ) - { - mListener = listener; - } - - /** - * Registers a listener to receive frequency correction resets - */ - public void setListener( FrequencyCorrectionResetListener listener ) - { - mResetListener = listener; - } - - /** - * Listener for source frequency or frequency offset value changes to - * reset this frequency controller value to zero. - */ - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - Event attribute = event.getEvent(); - - switch( attribute ) - { - //Direct the listener to reset when frequency correction (PPM) or - //sample rate changes - case FREQUENCY_CORRECTION_CHANGE_NOTIFICATION: - case SAMPLE_RATE_CHANGE_NOTIFICATION: - reset(); - break; - default: - break; - } - } - - /** - * Resets frequency correction value to zero and notifies any reset - * listeners - */ - public void reset() - { - if( mResetListener != null ) - { - mResetListener.resetFrequencyCorrection(); - } - } - - /** - * Sets frequency correction to the specified value and broadcasts a change - * request - */ - public void setFrequencyCorrection( int correction ) - { - mChannelFrequencyCorrection = correction; - - /* Limit frequency correction to +/- max correction value */ - if( mChannelFrequencyCorrection > mMaximumCorrection ) - { - mChannelFrequencyCorrection = mMaximumCorrection; - } - else if( mChannelFrequencyCorrection < -mMaximumCorrection ) - { - mChannelFrequencyCorrection = -mMaximumCorrection; - } - - /* Broadcast a change request */ - if( mListener != null ) - { - mListener.frequencyChanged( new FrequencyChangeEvent( - Event.CHANNEL_FREQUENCY_CORRECTION_CHANGE_REQUEST, - mChannelFrequencyCorrection ) ); - } - } - - /** - * Increments the frequency error correction value by the specified adjustment - */ - public void adjust( int adjustment ) - { - setFrequencyCorrection( getErrorCorrection() + adjustment ); - } - - /** - * Current frequency error correction value - * @return - */ - public int getErrorCorrection() - { - return mChannelFrequencyCorrection; - } - - /** - * Listener interface to be notified when this control is reset to a zero - * frequency correction value - */ - public interface FrequencyCorrectionResetListener - { - public void resetFrequencyCorrection(); - } -} diff --git a/src/source/tuner/frequency/FrequencyChangeListener.java b/src/source/tuner/frequency/IFrequencyChangeListener.java similarity index 90% rename from src/source/tuner/frequency/FrequencyChangeListener.java rename to src/source/tuner/frequency/IFrequencyChangeListener.java index fab9f28a..99b75804 100644 --- a/src/source/tuner/frequency/FrequencyChangeListener.java +++ b/src/source/tuner/frequency/IFrequencyChangeListener.java @@ -17,6 +17,8 @@ ******************************************************************************/ package source.tuner.frequency; +import sample.Listener; + /** * Interface for broadcasting changes to frequency, bandwidth, sample rate, @@ -26,7 +28,7 @@ package source.tuner.frequency; * frequency value. All frequency correction aspects should be handled within * the device that implements the tuning of the frequency. */ -public interface FrequencyChangeListener +public interface IFrequencyChangeListener { - public void frequencyChanged( FrequencyChangeEvent event ); + public Listener getFrequencyChangeListener(); } diff --git a/src/source/tuner/frequency/IFrequencyChangeProcessor.java b/src/source/tuner/frequency/IFrequencyChangeProcessor.java new file mode 100644 index 00000000..479d8e8d --- /dev/null +++ b/src/source/tuner/frequency/IFrequencyChangeProcessor.java @@ -0,0 +1,6 @@ +package source.tuner.frequency; + +public interface IFrequencyChangeProcessor +{ + public void frequencyChanged( FrequencyChangeEvent event ); +} diff --git a/src/source/tuner/frequency/IFrequencyChangeProvider.java b/src/source/tuner/frequency/IFrequencyChangeProvider.java new file mode 100644 index 00000000..919126e7 --- /dev/null +++ b/src/source/tuner/frequency/IFrequencyChangeProvider.java @@ -0,0 +1,9 @@ +package source.tuner.frequency; + +import sample.Listener; + +public interface IFrequencyChangeProvider +{ + public void setFrequencyChangeListener( Listener listener ); + public void removeFrequencyChangeListener(); +} diff --git a/src/source/tuner/frequency/IFrequencyCorrectionController.java b/src/source/tuner/frequency/IFrequencyCorrectionController.java deleted file mode 100644 index e937bd4a..00000000 --- a/src/source/tuner/frequency/IFrequencyCorrectionController.java +++ /dev/null @@ -1,8 +0,0 @@ -package source.tuner.frequency; - - -public interface IFrequencyCorrectionController -{ - public boolean hasFrequencyCorrectionControl(); - public FrequencyCorrectionControl getFrequencyCorrectionControl(); -} diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java index 4b38c4bd..154c55bc 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -26,6 +26,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; +import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; import source.tuner.TunerChannel; @@ -34,7 +35,6 @@ import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.hackrf.HackRFTunerController.BoardID; -import controller.ResourceManager; import controller.ThreadPoolManager; public class HackRFTuner extends Tuner @@ -76,9 +76,9 @@ public class HackRFTuner extends Tuner } @Override - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { - return new HackRFTunerEditorPanel( this, resourceManager ); + return new HackRFTunerEditorPanel( this, settingsManager ); } @Override diff --git a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java index b5ec8acb..1acd7269 100644 --- a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java +++ b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java @@ -48,6 +48,7 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerConfigurationAssignment; @@ -55,7 +56,6 @@ import source.tuner.TunerType; import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; -import controller.ResourceManager; public class HackRFTunerConfigurationPanel extends JPanel { @@ -64,7 +64,7 @@ public class HackRFTunerConfigurationPanel extends JPanel private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private HackRFTunerController mController; private HackRFTunerConfiguration mSelectedConfig; @@ -81,10 +81,10 @@ public class HackRFTunerConfigurationPanel extends JPanel private JComboBox mComboSampleRate; - public HackRFTunerConfigurationPanel( ResourceManager resourceManager, + public HackRFTunerConfigurationPanel( SettingsManager settingsManager, HackRFTunerController controller ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mController = controller; init(); @@ -119,8 +119,7 @@ public class HackRFTunerConfigurationPanel extends JPanel if( serial != null ) { - savedConfig = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( + savedConfig = mSettingsManager.getSelectedTunerConfiguration( TunerType.HACKRF, serial ); } @@ -139,7 +138,7 @@ public class HackRFTunerConfigurationPanel extends JPanel if( serial != null ) { - mResourceManager.getSettingsManager() + mSettingsManager .setSelectedTunerConfiguration( TunerType.HACKRF, serial, mSelectedConfig ); } @@ -418,7 +417,7 @@ public class HackRFTunerConfigurationPanel extends JPanel public void actionPerformed( ActionEvent e ) { TunerConfiguration config = - mResourceManager.getSettingsManager() + mSettingsManager .addNewTunerConfiguration( TunerType.HACKRF, "New Configuration" ); @@ -458,7 +457,7 @@ public class HackRFTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mResourceManager.getSettingsManager() + mSettingsManager .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -494,7 +493,7 @@ public class HackRFTunerConfigurationPanel extends JPanel mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( TunerType.HACKRF, mController.getSerial().getSerialNumber(), config ); } catch ( UsbException | SourceException e1 ) @@ -518,7 +517,7 @@ public class HackRFTunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.HACKRF ); DefaultComboBoxModel model = @@ -538,7 +537,7 @@ public class HackRFTunerConfigurationPanel extends JPanel private HackRFTunerConfiguration getNamedConfiguration( String name ) { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.HACKRF ); for( TunerConfiguration config: configs ) @@ -557,6 +556,6 @@ public class HackRFTunerConfigurationPanel extends JPanel */ private void save() { - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } } diff --git a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java index 9c4d8490..fe32f16a 100644 --- a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java +++ b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java @@ -30,18 +30,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import source.tuner.hackrf.HackRFTunerController.BoardID; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - -public class HackRFTunerEditorPanel extends JPanel - implements FrequencyChangeListener +public class HackRFTunerEditorPanel extends JPanel implements IFrequencyChangeProcessor { private static final long serialVersionUID = 1L; @@ -52,14 +50,14 @@ public class HackRFTunerEditorPanel extends JPanel private HackRFTuner mTuner; private HackRFTunerController mController; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; public HackRFTunerEditorPanel( HackRFTuner tuner, - ResourceManager resourceManager ) + SettingsManager resourceManager ) { mTuner = tuner; mController = mTuner.getController(); - mResourceManager = resourceManager; + mSettingsManager = resourceManager; initGUI(); } @@ -89,7 +87,7 @@ public class HackRFTunerEditorPanel extends JPanel /* Add frequency control as frequency change listener. This creates a * feedback loop, so the control does not rebroadcast the event */ - mTuner.addListener( mFrequencyControl ); + mTuner.addFrequencyChangeProcessor( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); @@ -102,7 +100,7 @@ public class HackRFTunerEditorPanel extends JPanel add( tabs, "span,grow,push" ); tabs.add( "Config", new HackRFTunerConfigurationPanel( - mResourceManager, mTuner.getController() ) ); + mSettingsManager, mTuner.getController() ) ); tabs.add( "Info", new HackRFInformationPanel( mTuner.getController() ) ); } @@ -110,7 +108,7 @@ public class HackRFTunerEditorPanel extends JPanel @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { try { diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 02634918..4af1a8d7 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -27,6 +27,7 @@ import org.usb4java.LibUsbException; import sample.Listener; import sample.complex.ComplexBuffer; +import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; import source.tuner.TunerChannel; @@ -34,9 +35,8 @@ import source.tuner.TunerChannelSource; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeListener; import source.tuner.rtl.RTL2832TunerController.SampleRate; -import controller.ResourceManager; import controller.ThreadPoolManager; public class RTL2832Tuner extends Tuner @@ -57,7 +57,7 @@ public class RTL2832Tuner extends Tuner mTunerClass = tunerClass; mController = controller; - mController.addListener( (FrequencyChangeListener)this ); + mController.addListener( this ); } public void dispose() @@ -89,9 +89,9 @@ public class RTL2832Tuner extends Tuner } @Override - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { - return mController.getEditor( resourceManager ); + return mController.getEditor( settingsManager ); } @Override diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index d419c782..e972c1d0 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -52,13 +52,11 @@ import sample.Broadcaster; import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerController; import source.tuner.TunerType; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; import buffer.FloatAveragingBuffer; -import controller.ResourceManager; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; @@ -364,7 +362,7 @@ public abstract class RTL2832TunerController extends TunerController } } - public abstract JPanel getEditor( ResourceManager resourceManager ); + public abstract JPanel getEditor( SettingsManager settingsManager ); public abstract void setSampleRateFilters( int sampleRate ) throws SourceException; diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java index cce675b4..ed3a4ac7 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java @@ -48,6 +48,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerConfigurationAssignment; @@ -57,7 +58,6 @@ import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; import source.tuner.rtl.e4k.E4KTunerController.E4KGain; import source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; -import controller.ResourceManager; public class E4KTunerConfigurationPanel extends JPanel { @@ -65,7 +65,7 @@ public class E4KTunerConfigurationPanel extends JPanel private final static Logger mLog = LoggerFactory.getLogger( E4KTunerConfigurationPanel.class ); - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private E4KTunerController mController; private E4KTunerConfiguration mSelectedConfig; @@ -84,10 +84,10 @@ public class E4KTunerConfigurationPanel extends JPanel private JComboBox mComboSampleRate; - public E4KTunerConfigurationPanel( ResourceManager resourceManager, + public E4KTunerConfigurationPanel( SettingsManager settingsManager, E4KTunerController controller ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mController = controller; init(); @@ -108,8 +108,8 @@ public class E4KTunerConfigurationPanel extends JPanel mComboConfigurations.setModel( getModel() ); /* Determine which tuner configuration should be selected/displayed */ - TunerConfigurationAssignment savedConfig = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( + TunerConfigurationAssignment savedConfig = mSettingsManager + .getSelectedTunerConfiguration( TunerType.ELONICS_E4000, mController.getUniqueID() ); if( savedConfig != null ) @@ -124,7 +124,7 @@ public class E4KTunerConfigurationPanel extends JPanel mSelectedConfig = mComboConfigurations.getItemAt( 0 ); //Store this config as the default for this tuner at this address - mResourceManager.getSettingsManager() + mSettingsManager .setSelectedTunerConfiguration( TunerType.ELONICS_E4000, mController.getUniqueID(), mSelectedConfig ); @@ -168,7 +168,7 @@ public class E4KTunerConfigurationPanel extends JPanel /* Reset this named config as the assigned config for this * tuner */ - mResourceManager.getSettingsManager() + mSettingsManager .setSelectedTunerConfiguration( TunerType.ELONICS_E4000, mController.getUniqueID(), mSelectedConfig ); @@ -542,7 +542,7 @@ public class E4KTunerConfigurationPanel extends JPanel public void actionPerformed( ActionEvent e ) { TunerConfiguration config = - mResourceManager.getSettingsManager() + mSettingsManager .addNewTunerConfiguration( TunerType.ELONICS_E4000, "New Configuration" ); @@ -582,7 +582,7 @@ public class E4KTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mResourceManager.getSettingsManager() + mSettingsManager .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -623,7 +623,7 @@ public class E4KTunerConfigurationPanel extends JPanel mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( TunerType.ELONICS_E4000, mController.getUniqueID(), config ); } catch ( SourceException e1 ) @@ -647,7 +647,7 @@ public class E4KTunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.ELONICS_E4000 ); DefaultComboBoxModel model = @@ -667,7 +667,7 @@ public class E4KTunerConfigurationPanel extends JPanel private E4KTunerConfiguration getNamedConfiguration( String name ) { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.ELONICS_E4000 ); for( TunerConfiguration config: configs ) @@ -686,6 +686,6 @@ public class E4KTunerConfigurationPanel extends JPanel */ private void save() { - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } } diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index 6bdfb149..b727fa74 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -30,11 +30,11 @@ import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.rtl.RTL2832TunerController; -import controller.ResourceManager; import controller.ThreadPoolManager; public class E4KTunerController extends RTL2832TunerController @@ -252,11 +252,11 @@ public class E4KTunerController extends RTL2832TunerController } } - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { if( mEditor == null ) { - mEditor = new E4KTunerEditorPanel( this, resourceManager ); + mEditor = new E4KTunerEditorPanel( this, settingsManager ); } return mEditor; diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java b/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java index 5c883748..fe797d55 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java +++ b/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java @@ -30,17 +30,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import source.tuner.rtl.RTL2832InfoPanel; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - -public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListener +public class E4KTunerEditorPanel extends JPanel implements IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( E4KTunerEditorPanel.class ); @@ -48,15 +47,15 @@ public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListen private static final long serialVersionUID = 1L; private E4KTunerController mController; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private JFrequencyControl mFrequencyControl; public E4KTunerEditorPanel( E4KTunerController controller, - ResourceManager resourceManager ) + SettingsManager settingsManager ) { mController = controller; - mResourceManager = resourceManager; + mSettingsManager = settingsManager; initGUI(); } @@ -84,7 +83,7 @@ public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListen tabs.setForeground( Color.BLACK ); tabs.addTab( "Configuration", - new E4KTunerConfigurationPanel( mResourceManager, mController ) ); + new E4KTunerConfigurationPanel( mSettingsManager, mController ) ); tabs.addTab( "Info", new RTL2832InfoPanel( mController ) ); @@ -98,7 +97,7 @@ public class E4KTunerEditorPanel extends JPanel implements FrequencyChangeListen @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { final long frequency = event.getValue().longValue(); diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java index fe08d0c3..413dc8ea 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java @@ -48,6 +48,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerConfigurationAssignment; @@ -57,7 +58,6 @@ import source.tuner.rtl.r820t.R820TTunerController.R820TGain; import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; -import controller.ResourceManager; public class R820TTunerConfigurationPanel extends JPanel { @@ -67,7 +67,7 @@ public class R820TTunerConfigurationPanel extends JPanel private static final long serialVersionUID = 1L; private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private R820TTunerController mController; private R820TTunerConfiguration mSelectedConfig; @@ -86,10 +86,10 @@ public class R820TTunerConfigurationPanel extends JPanel private JComboBox mComboSampleRate; - public R820TTunerConfigurationPanel( ResourceManager resourceManager, + public R820TTunerConfigurationPanel( SettingsManager settingsManager, R820TTunerController controller ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; mController = controller; init(); @@ -110,8 +110,8 @@ public class R820TTunerConfigurationPanel extends JPanel mComboConfigurations.setModel( getModel() ); /* Determine which tuner configuration should be selected/displayed */ - TunerConfigurationAssignment savedConfig = mResourceManager - .getSettingsManager().getSelectedTunerConfiguration( + TunerConfigurationAssignment savedConfig = mSettingsManager + .getSelectedTunerConfiguration( TunerType.RAFAELMICRO_R820T, mController.getUniqueID() ); if( savedConfig != null ) @@ -126,7 +126,7 @@ public class R820TTunerConfigurationPanel extends JPanel mSelectedConfig = mComboConfigurations.getItemAt( 0 ); //Store this config as the default for this tuner at this address - mResourceManager.getSettingsManager() + mSettingsManager .setSelectedTunerConfiguration( TunerType.RAFAELMICRO_R820T, mController.getUniqueID(), mSelectedConfig ); @@ -549,7 +549,7 @@ public class R820TTunerConfigurationPanel extends JPanel public void actionPerformed( ActionEvent e ) { TunerConfiguration config = - mResourceManager.getSettingsManager() + mSettingsManager .addNewTunerConfiguration( TunerType.RAFAELMICRO_R820T, "New Configuration" ); @@ -589,7 +589,7 @@ public class R820TTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mResourceManager.getSettingsManager() + mSettingsManager .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -630,7 +630,7 @@ public class R820TTunerConfigurationPanel extends JPanel mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + mSettingsManager.setSelectedTunerConfiguration( TunerType.RAFAELMICRO_R820T, mController.getUniqueID(), config ); } catch ( SourceException e1 ) @@ -654,7 +654,7 @@ public class R820TTunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); DefaultComboBoxModel model = @@ -674,7 +674,7 @@ public class R820TTunerConfigurationPanel extends JPanel private R820TTunerConfiguration getNamedConfiguration( String name ) { ArrayList configs = - mResourceManager.getSettingsManager() + mSettingsManager .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); for( TunerConfiguration config: configs ) @@ -693,6 +693,6 @@ public class R820TTunerConfigurationPanel extends JPanel */ private void save() { - mResourceManager.getSettingsManager().save(); + mSettingsManager.save(); } } diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java index 4713697d..109458e2 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/source/tuner/rtl/r820t/R820TTunerController.java @@ -33,11 +33,11 @@ import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import org.usb4java.LibUsbException; +import settings.SettingsManager; import source.SourceException; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.rtl.RTL2832TunerController; -import controller.ResourceManager; import controller.ThreadPoolManager; public class R820TTunerController extends RTL2832TunerController @@ -139,11 +139,11 @@ public class R820TTunerController extends RTL2832TunerController /** * Controller gui editor component for this tuner */ - public JPanel getEditor( ResourceManager resourceManager ) + public JPanel getEditor( SettingsManager settingsManager ) { if( mEditor == null ) { - mEditor = new R820TTunerEditorPanel( this, resourceManager ); + mEditor = new R820TTunerEditorPanel( this, settingsManager ); } return mEditor; diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java b/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java index f59d29a7..c761a07a 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java @@ -30,17 +30,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import settings.SettingsManager; import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; import source.tuner.frequency.FrequencyChangeEvent.Event; +import source.tuner.frequency.IFrequencyChangeProcessor; import source.tuner.rtl.RTL2832InfoPanel; import com.jidesoft.swing.JideTabbedPane; -import controller.ResourceManager; - -public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeListener +public class R820TTunerEditorPanel extends JPanel implements IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( R820TTunerEditorPanel.class ); @@ -48,15 +47,15 @@ public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeList private static final long serialVersionUID = 1L; private R820TTunerController mController; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; private JFrequencyControl mFrequencyControl; public R820TTunerEditorPanel( R820TTunerController controller, - ResourceManager resourceManager ) + SettingsManager settingsManager ) { mController = controller; - mResourceManager = resourceManager; + mSettingsManager = settingsManager; initGUI(); } @@ -83,7 +82,7 @@ public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeList tabs.setForeground( Color.BLACK ); tabs.addTab( "Configuration", - new R820TTunerConfigurationPanel( mResourceManager, mController ) ); + new R820TTunerConfigurationPanel( mSettingsManager, mController ) ); tabs.addTab( "Info", new RTL2832InfoPanel( mController ) ); @@ -97,7 +96,7 @@ public class R820TTunerEditorPanel extends JPanel implements FrequencyChangeList @Override public void frequencyChanged( FrequencyChangeEvent event ) { - if( event.getEvent() == Event.FREQUENCY_CHANGE_NOTIFICATION ) + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { final long frequency = event.getValue().longValue(); diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index 235c4717..9e4a65a8 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -51,11 +51,42 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil public ComplexWaveSource( File file ) throws IOException { - super( file.getAbsolutePath() ); - mFile = file; } + @Override + public void reset() + { + stop(); + start(); + } + + @Override + public void start() + { + try + { + open(); + } + catch ( IOException | UnsupportedAudioFileException e ) + { + mLog.error( "Error starting complex wave source" ); + } + } + + @Override + public void stop() + { + try + { + close(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping complex wave source" ); + } + } + @Override public long getFrameCount() throws IOException { diff --git a/src/source/wave/RealWaveSource.java b/src/source/wave/RealWaveSource.java index a7251674..9af020c1 100644 --- a/src/source/wave/RealWaveSource.java +++ b/src/source/wave/RealWaveSource.java @@ -52,11 +52,46 @@ public class RealWaveSource extends RealSource public RealWaveSource( File file ) throws IOException { - super( file.getAbsolutePath() ); - mFile = file; } + + @Override + public void reset() + { + stop(); + start(); + } + + + @Override + public void start() + { + try + { + open(); + } + catch ( IOException | UnsupportedAudioFileException e ) + { + mLog.error( "Error starting real wave source", e ); + } + } + + + @Override + public void stop() + { + try + { + close(); + } + catch ( IOException e ) + { + mLog.error( "Error stopping real wave source", e ); + } + } + + @Override public long getFrameCount() throws IOException { diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index 5a87dc4c..9a2b134f 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -6,7 +6,6 @@ import java.awt.event.ComponentListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; -import java.util.Arrays; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.JLayeredPane; @@ -16,15 +15,16 @@ import javax.swing.JPopupMenu; import javax.swing.JSeparator; import javax.swing.SwingUtilities; +import module.ProcessingChain; +import net.miginfocom.swing.MigLayout; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import net.miginfocom.swing.MigLayout; import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; import sample.real.RealBuffer; -import sample.real.RealSampleListener; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.Setting; @@ -40,15 +40,13 @@ import spectrum.menu.FFTWindowTypeItem; import spectrum.menu.FrameRateItem; import spectrum.menu.SmoothingItem; import spectrum.menu.SmoothingTypeItem; -import controller.ResourceManager; import controller.channel.Channel; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; +import controller.channel.ChannelProcessingManager; import dsp.filter.Filters; -import dsp.filter.FloatHalfBandFilter; import dsp.filter.Window.WindowType; import dsp.filter.halfband.real.HalfBandFilter_RB_RB; -import dsp.filter.hilbert.HilbertTransform; import dsp.filter.smoothing.SmoothingFilter.SmoothingType; public class ChannelSpectrumPanel extends JPanel @@ -61,7 +59,6 @@ public class ChannelSpectrumPanel extends JPanel private static final long serialVersionUID = 1L; -// private ResourceManager mResourceManager; private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.REAL ); private DFTResultsConverter mDFTConverter = new RealDecibelConverter(); private JLayeredPane mLayeredPane; @@ -80,10 +77,13 @@ public class ChannelSpectrumPanel extends JPanel private AtomicBoolean mEnabled = new AtomicBoolean(); private SettingsManager mSettingsManager; + private ChannelProcessingManager mChannelProcessingManager; - public ChannelSpectrumPanel( SettingsManager settingsManager ) + public ChannelSpectrumPanel( SettingsManager settingsManager, + ChannelProcessingManager channelProcessingManager ) { mSettingsManager = settingsManager; + mChannelProcessingManager = channelProcessingManager; if( mSettingsManager != null ) { @@ -99,7 +99,7 @@ public class ChannelSpectrumPanel extends JPanel /* Set the DFTProcessor to the decimated 24kHz sample rate */ mDFTProcessor.frequencyChanged( - new FrequencyChangeEvent( Event.SAMPLE_RATE_CHANGE_NOTIFICATION, 24000 ) ); + new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, 24000 ) ); initGui(); } @@ -167,14 +167,17 @@ public class ChannelSpectrumPanel extends JPanel switch( event.getEvent() ) { case NOTIFICATION_SELECTION_CHANGE: - if( !event.getChannel().isSelected() ) + //ChannelSelectionManager ensures that only 1 channel can be + //selected and any previously selected channel will be first + //deselected before we get a new selection event + if( event.getChannel().isSelected() ) { - stop(); + if( mCurrentChannel != null ) + { + stop(); + mCurrentChannel = null; + } - mCurrentChannel = null; - } - else - { mCurrentChannel = event.getChannel(); if( mEnabled.get() ) @@ -182,17 +185,13 @@ public class ChannelSpectrumPanel extends JPanel start(); } } - if( event.getChannel().isSelected() && - mCurrentChannel != event.getChannel() ) + else { stop(); - - mCurrentChannel = event.getChannel(); - - start(); + mCurrentChannel = null; } break; - case REQUEST_DISABLE: + case NOTIFICATION_PROCESSING_STOP: if( event.getChannel() == mCurrentChannel ) { if( mEnabled.get() ) @@ -203,32 +202,36 @@ public class ChannelSpectrumPanel extends JPanel mCurrentChannel = null; } break; - case REQUEST_ENABLE: - if( event.getChannel() == mCurrentChannel && - !event.getChannel().isProcessing() ) - { - stop(); - mCurrentChannel = null; - } - break; } } private void start() { - if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() ) + if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.getEnabled() ) { - mCurrentChannel.getProcessingChain().addRealBufferListener( this ); - mDFTProcessor.start(); + ProcessingChain processingChain = mChannelProcessingManager + .getProcessingChain( mCurrentChannel ); + + if( processingChain != null ) + { + processingChain.addRealBufferListener( this ); + + mDFTProcessor.start(); + } } } private void stop() { - - if( mCurrentChannel != null && mCurrentChannel.isProcessing() ) + if( mCurrentChannel != null && mCurrentChannel.getEnabled() ) { - mCurrentChannel.getProcessingChain().removeRealBufferListener( this ); + ProcessingChain processingChain = mChannelProcessingManager + .getProcessingChain( mCurrentChannel ); + + if( processingChain != null ) + { + processingChain.removeRealBufferListener( this ); + } } mDFTProcessor.stop(); diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index ef9b745f..207d9eb9 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -36,7 +36,7 @@ import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import spectrum.converter.DFTResultsConverter; import controller.NamingThreadFactory; import dsp.filter.Window; @@ -47,7 +47,7 @@ import dsp.filter.Window.WindowType; * of DFT results, using configurable fft size and output dispatch timelines. */ public class DFTProcessor implements Listener, - FrequencyChangeListener, + IFrequencyChangeProcessor, IDFTWidthChangeProcessor { private final static Logger mLog = @@ -494,7 +494,7 @@ public class DFTProcessor implements Listener, { switch( event.getEvent() ) { - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: mSampleRate = event.getValue().intValue(); calculateConsumptionRate(); break; diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index f51f744d..7e1aadf1 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -46,7 +46,7 @@ import settings.SettingChangeListener; import settings.SettingsManager; import source.tuner.TunerChannel; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; @@ -55,7 +55,7 @@ import controller.channel.ChannelModel; public class OverlayPanel extends JPanel implements ChannelEventListener, - FrequencyChangeListener, + IFrequencyChangeProcessor, SettingChangeListener { private static final long serialVersionUID = 1L; @@ -528,14 +528,14 @@ public class OverlayPanel extends JPanel { if( mChannelDisplay == ChannelDisplay.ALL || ( mChannelDisplay == ChannelDisplay.ENABLED && - channel.isProcessing() ) ) + channel.getEnabled() ) ) { //Choose the correct background color to use if( channel.isSelected() ) { graphics.setColor( mColorChannelConfigSelected ); } - else if( channel.isProcessing() ) + else if( channel.getEnabled() ) { graphics.setColor( mColorChannelConfigProcessing ); } @@ -613,9 +613,11 @@ public class OverlayPanel extends JPanel drawChannelCenterLine( graphics, frequencyAxis ); /* Draw Automatic Frequency Control line */ - if( channel.hasFrequencyControl() ) + int correction = channel.getChannelFrequencyCorrection(); + + if( correction != 0 ) { - long error = frequency + channel.getFrequencyCorrection(); + long error = frequency + correction; drawAFC( graphics, frequencyAxis, getAxisFromFrequency( error ) ); } @@ -674,11 +676,11 @@ public class OverlayPanel extends JPanel { switch( event.getEvent() ) { - case SAMPLE_RATE_CHANGE_NOTIFICATION: + case NOTIFICATION_SAMPLE_RATE_CHANGE: mBandwidth = event.getValue().intValue(); mLabelSizeMonitor.update(); break; - case FREQUENCY_CHANGE_NOTIFICATION: + case NOTIFICATION_FREQUENCY_CHANGE: mFrequency = event.getValue().longValue(); mLabelSizeMonitor.update(); break; diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index e03d27e9..f8d1acd9 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -66,7 +66,7 @@ import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.FrequencyChangeListener; +import source.tuner.frequency.IFrequencyChangeProcessor; import spectrum.OverlayPanel.ChannelDisplay; import spectrum.converter.ComplexDecibelConverter; import spectrum.converter.DFTResultsConverter; @@ -82,13 +82,14 @@ import com.jidesoft.swing.JideSplitPane; import controller.ConfigurationControllerModel; import controller.channel.Channel; import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; import controller.channel.ChannelUtils; import dsp.filter.Window.WindowType; import dsp.filter.smoothing.SmoothingFilter.SmoothingType; public class SpectralDisplayPanel extends JPanel implements Listener, - FrequencyChangeListener, + IFrequencyChangeProcessor, IDFTWidthChangeProcessor, TunerSelectionListener { @@ -115,9 +116,11 @@ public class SpectralDisplayPanel extends JPanel private OverlayPanel mOverlayPanel; private DFTProcessor mDFTProcessor; private DFTResultsConverter mDFTConverter; + private ConfigurationControllerModel mControllerModel; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; - private ConfigurationControllerModel mController; private Tuner mTuner; /** @@ -132,23 +135,26 @@ public class SpectralDisplayPanel extends JPanel * the DFT is translated to decibels for display in the spectrum and * waterfall components. */ - public SpectralDisplayPanel( ChannelModel channelModel, - ConfigurationControllerModel controller, + public SpectralDisplayPanel( ConfigurationControllerModel controllerModel, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, PlaylistManager playlistManager, SettingsManager settingsManager ) { - mController = controller; + mControllerModel = controllerModel; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; mPlaylistManager = playlistManager; mSettingsManager = settingsManager; mSpectrumPanel = new SpectrumPanel( mSettingsManager ); - mOverlayPanel = new OverlayPanel( mSettingsManager, channelModel ); + mOverlayPanel = new OverlayPanel( mSettingsManager, mChannelModel ); mWaterfallPanel = new WaterfallPanel( mSettingsManager ); //Register to receive tuner selection events - if( mController != null ) + if( mControllerModel != null ) { - mController.addListener( (TunerSelectionListener)this ); + mControllerModel.addListener( (TunerSelectionListener)this ); } init(); @@ -189,12 +195,12 @@ public class SpectralDisplayPanel extends JPanel /* De-register from receiving samples when the window closes */ clearTuner(); - if( mController != null ) + if( mControllerModel != null ) { - mController.removeListener( (TunerSelectionListener)this ); + mControllerModel.removeListener( (TunerSelectionListener)this ); } - mController = null; + mControllerModel = null; mPlaylistManager = null; mSettingsManager = null; @@ -426,7 +432,6 @@ public class SpectralDisplayPanel extends JPanel /** * Receives frequency change events -- primarily from tuner components. */ - @Override public void frequencyChanged( FrequencyChangeEvent event ) { mOverlayPanel.frequencyChanged( event ); @@ -458,7 +463,7 @@ public class SpectralDisplayPanel extends JPanel if( mTuner != null ) { //Register to receive frequency change events - mTuner.addListener( (FrequencyChangeListener)this ); + mTuner.addFrequencyChangeProcessor( this ); //Register the dft processor to receive samples from the tuner mTuner.addListener( (Listener)mDFTProcessor ); @@ -470,10 +475,10 @@ public class SpectralDisplayPanel extends JPanel try { frequencyChanged( new FrequencyChangeEvent( - Event.FREQUENCY_CHANGE_NOTIFICATION, mTuner.getFrequency() ) ); + Event.NOTIFICATION_FREQUENCY_CHANGE, mTuner.getFrequency() ) ); frequencyChanged( new FrequencyChangeEvent( - Event.SAMPLE_RATE_CHANGE_NOTIFICATION, mTuner.getSampleRate() ) ); + Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getSampleRate() ) ); } catch ( SourceException e ) { @@ -491,7 +496,7 @@ public class SpectralDisplayPanel extends JPanel if( mTuner != null ) { //Deregister for frequency change events from the tuner - mTuner.removeListener( (FrequencyChangeListener)this ); + mTuner.removeFrequencyChangeProcessor( this ); //Deregister the dft processor from receiving samples mTuner.removeListener( (Listener)mDFTProcessor ); @@ -652,8 +657,9 @@ public class SpectralDisplayPanel extends JPanel for( Channel channel: channels ) { - JMenu channelMenu = ChannelUtils.getContextMenu( - mPlaylistManager, channel, SpectralDisplayPanel.this ); + JMenu channelMenu = ChannelUtils.getContextMenu( mChannelModel, + mChannelProcessingManager, mPlaylistManager, channel, + SpectralDisplayPanel.this ); if( channelMenu != null ) { @@ -675,10 +681,7 @@ public class SpectralDisplayPanel extends JPanel for( DecoderType type: DecoderType.getPrimaryDecoders() ) { - decoderMenu.add( new DecoderItem( mController, - frequency, - type ) ); - + decoderMenu.add( new DecoderItem( type, frequency ) ); } frequencyMenu.add( decoderMenu ); @@ -957,17 +960,14 @@ public class SpectralDisplayPanel extends JPanel { private static final long serialVersionUID = 1L; - private ConfigurationControllerModel mController; + private ChannelModel mChannelModel; private long mFrequency; private DecoderType mDecoder; - public DecoderItem( ConfigurationControllerModel controller, - long frequency, - DecoderType type ) + public DecoderItem( DecoderType type, long frequency ) { super( type.getDisplayString() ); - mController = controller; mFrequency = frequency; mDecoder = type; @@ -976,7 +976,7 @@ public class SpectralDisplayPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - mController.createChannel( mFrequency, mDecoder ); + mChannelModel.createChannel( mDecoder, mFrequency ); } } ); } diff --git a/src/spectrum/SpectrumFrame.java b/src/spectrum/SpectrumFrame.java index aa39a2f2..f3e6eac4 100644 --- a/src/spectrum/SpectrumFrame.java +++ b/src/spectrum/SpectrumFrame.java @@ -28,18 +28,18 @@ import playlist.PlaylistManager; import settings.SettingsManager; import source.tuner.Tuner; import controller.ConfigurationControllerModel; -import controller.ResourceManager; import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; public class SpectrumFrame extends JFrame implements WindowListener { private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; private SpectralDisplayPanel mSpectralDisplayPanel; - public SpectrumFrame( ChannelModel channelModel, - ConfigurationControllerModel controller, + public SpectrumFrame( ConfigurationControllerModel controllerModel, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, PlaylistManager playlistManager, SettingsManager settingsManager, Tuner tuner ) @@ -50,8 +50,8 @@ public class SpectrumFrame extends JFrame implements WindowListener setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); - mSpectralDisplayPanel = new SpectralDisplayPanel( channelModel, - controller, playlistManager, settingsManager ); + mSpectralDisplayPanel = new SpectralDisplayPanel( controllerModel, + channelModel, channelProcessingManager, playlistManager, settingsManager ); mSpectralDisplayPanel.tunerSelected( tuner ); add( mSpectralDisplayPanel, "grow" ); From 7d1ec344dec76d7a9d5507fb01f8ae4e3830afed Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 11 Jan 2016 20:35:53 -0500 Subject: [PATCH 03/74] Work in progress - updates channel component editors and changes functionality so that the editor starts up without a channel displayed and as each channel row is selected in the channels table, the editor is updated with the channel configuration. Likewise, the channel editor is now a channel event listener on the channel model and will maintain channel state if the user changes channel configuration in any of the external channel controllers like the spectral display or the channel list. --- src/controller/ControllerPanel.java | 10 +- src/controller/channel/ChannelEditor.java | 88 ++++++++++---- .../channel/ChannelProcessingManager.java | 22 +++- .../channel/NameConfigurationEditor.java | 112 +++++++++++------- src/controller/channel/OldChannelEditor.java | 10 +- .../decode/AuxDecodeComponentEditor.java | 88 +++++++------- src/module/decode/DecodeComponentEditor.java | 75 +++++------- .../decode/state/ChannelStatePanel.java | 29 +++-- src/module/log/EventLogComponentEditor.java | 89 ++++++-------- src/playlist/PlaylistEditor.java | 27 +++-- src/record/RecordComponentEditor.java | 90 +++++++------- src/record/config/RecordConfiguration.java | 7 +- src/source/SourceComponentEditor.java | 57 ++++----- 13 files changed, 381 insertions(+), 323 deletions(-) diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index e8612478..bbe7273b 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -129,12 +129,12 @@ public class ControllerPanel extends JPanel mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont( this.getFont() ); mTabbedPane.setForeground( Color.BLACK ); + mTabbedPane.addTab( "Channels", mPlaylistEditor ); mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); mTabbedPane.addTab( "Messages", mMessageActivityPanel ); - - mTabbedPane.addTab( "Playlist", mPlaylistEditor ); + mTabbedPane.addTab( "Map", mMapPanel ); /** * Change listener to enable/disable the channel spectrum display @@ -160,11 +160,6 @@ public class ControllerPanel extends JPanel } } ); - /** - * Add mapping services and map panel to a new tab - */ - mTabbedPane.addTab( "Map", mMapPanel ); - /* Register each of the components to receive channel events when the * channels are selected or change */ mChannelModel.addListener( mCallEventPanel ); @@ -176,7 +171,6 @@ public class ControllerPanel extends JPanel channelStateListScroll.getViewport().setView( mChannelStateList ); channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) ); - JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); audioChannelListSplit.setDividerSize( 5 ); audioChannelListSplit.add( mAudioPanel ); diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index a76902d7..99387271 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,6 +20,7 @@ package controller.channel; import java.awt.Color; import java.awt.Component; +import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -63,11 +64,10 @@ public class ChannelEditor extends JPanel private RecordComponentEditor mRecordEditor; private SourceComponentEditor mSourceEditor; - private JTextField mChannelName; - private JCheckBox mChannelEnabled = new JCheckBox(); - private JComboBox mComboAliasLists; - private Channel mChannel; + + private JCheckBox mChannelEnabled = new JCheckBox(); + private ChannelModel mChannelModel; private PlaylistManager mPlaylistManager; private SourceManager mSourceManager; @@ -88,9 +88,18 @@ public class ChannelEditor extends JPanel initGUI(); } - public void setChannel( Channel channel ) + /** + * Sets the channel configuration in each of the channel editor components + * Note: this method must be invoked on the swing event dispatch thread + */ + public void setChannel( final Channel channel ) { mNameConfigurationEditor.setConfiguration( channel ); + mSourceEditor.setConfiguration( channel ); + mDecodeEditor.setConfiguration( channel ); + mAuxDecodeEditor.setConfiguration( channel ); + mEventLogEditor.setConfiguration( channel ); + mRecordEditor.setConfiguration( channel ); mChannel = channel; } @@ -103,27 +112,28 @@ public class ChannelEditor extends JPanel add( new JLabel( "Enabled:" ), "wrap" ); - add( mNameConfigurationEditor ); + add( mNameConfigurationEditor, "growx" ); JideTabbedPane tabs = new JideTabbedPane(); tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); tabs.addTab( "Name/Alias", mNameConfigurationEditor ); -// mSourceEditor = new SourceComponentEditor( mSourceManager, mChannel ); -// tabs.addTab( "Source", mSourceEditor ); -// -// mDecodeEditor = new DecodeComponentEditor( mPlaylistManager, mChannel ); -// tabs.addTab( "Decoder", mDecodeEditor ); -// -// mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannel ); -// tabs.addTab( "Aux", mAuxDecodeEditor ); -// -// mEventLogEditor = new EventLogComponentEditor( mChannel ); -// tabs.addTab( "Event Log", mEventLogEditor ); -// -// mRecordEditor = new RecordComponentEditor( mChannel ); -// tabs.addTab( "Record", mRecordEditor ); + + mSourceEditor = new SourceComponentEditor( mSourceManager ); + tabs.addTab( "Source", mSourceEditor ); + + mDecodeEditor = new DecodeComponentEditor( mPlaylistManager ); + tabs.addTab( "Decoder", mDecodeEditor ); + + mAuxDecodeEditor = new AuxDecodeComponentEditor(); + tabs.addTab( "Aux Decoders", mAuxDecodeEditor ); + + mEventLogEditor = new EventLogComponentEditor(); + tabs.addTab( "Logging", mEventLogEditor ); + + mRecordEditor = new RecordComponentEditor(); + tabs.addTab( "Recording", mRecordEditor ); add( tabs, "span,grow,push" ); @@ -139,7 +149,28 @@ public class ChannelEditor extends JPanel @Override public void channelChanged( ChannelEvent event ) { + if( !EventQueue.isDispatchThread() ) + { + mLog.debug( "Not on the swing thread!!!!" ); + } + if( mChannel != null && mChannel == event.getChannel() ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + setChannel( mChannel ); + break; + case NOTIFICATION_DELETE: + mChannel = null; + setChannel( mChannel ); + break; + default: + break; + } + } } @Override @@ -163,6 +194,17 @@ public class ChannelEditor extends JPanel { //Validate each of the configuration editors to check for errors mNameConfigurationEditor.validateConfiguration(); + mSourceEditor.validateConfiguration(); + mDecodeEditor.validateConfiguration(); + mAuxDecodeEditor.validateConfiguration(); + mEventLogEditor.validateConfiguration(); + + //Validate the source configuration against the decode editor to + //ensure that the source type is compatible with the decode type + + //Validate the aux decoders against the primary decoder +//TODO: make sure that no aux decoders are running with P25 + // /** // * Validate the source configuration against the other component // * configuration editors @@ -207,6 +249,10 @@ public class ChannelEditor extends JPanel //Save the contents of each editor to the channel configuration mNameConfigurationEditor.save(); + mSourceEditor.save(); + mDecodeEditor.save(); + mAuxDecodeEditor.save(); + mEventLogEditor.save(); //Broadcast a channel configuration change event mChannelModel.broadcast( new ChannelEvent( mChannel, diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index 9bdb727e..7c367040 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -255,14 +255,32 @@ public class ChannelProcessingManager implements ChannelEventListener channel.setEnabled( true ); + mProcessingChains.put( channel.getChannelID(), processingChain ); + mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_START ) ); } private void stopProcessing( Channel channel, boolean remove ) { - mChannelModel.broadcast( - new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) ); + if( mProcessingChains.containsKey( channel.getChannelID() ) ) + { + ProcessingChain chain = mProcessingChains.get( channel.getChannelID() ); + + chain.stop(); + + mChannelModel.broadcast( + new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) ); + + if( remove ) + { + mProcessingChains.remove( channel.getChannelID() ); + + chain.dispose(); + } + } + + channel.setEnabled( false ); } /** diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java index c7c171ec..fe30282b 100644 --- a/src/controller/channel/NameConfigurationEditor.java +++ b/src/controller/channel/NameConfigurationEditor.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package controller.channel; import java.awt.event.ActionEvent; @@ -13,9 +30,6 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; import alias.AliasList; @@ -24,14 +38,14 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class ); + private static final String DEFAULT_NAME = "Please select a channel to view/edit"; private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); private ChannelModel mChannelModel; private PlaylistManager mPlaylistManager; - private JTextField mChannelName = new JTextField( "Please select a channel to edit" ); + private JTextField mChannelName = new JTextField( DEFAULT_NAME ); private JComboBox mSystemNameCombo = new JComboBox<>( EMPTY_MODEL ); private JComboBox mSiteNameCombo = new JComboBox<>( EMPTY_MODEL ); private JComboBox mAliasListCombo; @@ -44,7 +58,7 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor mChannelModel = model; mPlaylistManager = playlistManager; - setLayout( new MigLayout( "fill,wrap 2", "[right][]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][]", "[][][][][][grow]" ) ); add( new JLabel( "Name:" ) ); add( mChannelName, "growx" ); @@ -156,7 +170,11 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor mChannel.setSystem( ( system == null ? null : (String)system ) ); Object site = mSiteNameCombo.getSelectedItem(); - mChannel.setSystem( ( site == null ? null : (String)site ) ); + mChannel.setSite( ( site == null ? null : (String)site ) ); + + Object aliasList = mAliasListCombo.getSelectedItem(); + mChannel.setAliasListName( aliasList == null ? null : + ((AliasList)aliasList).getName() ); } } @@ -178,44 +196,54 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor { mChannel = channel; - mChannelName.setText( channel.getName() ); - - List systems = mChannelModel.getSystems(); - - if( systems.isEmpty() ) + if( mChannel != null ) { + mChannelName.setText( channel.getName() ); + + List systems = mChannelModel.getSystems(); + + if( systems.isEmpty() ) + { + mSystemNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSystemNameCombo.setModel( new DefaultComboBoxModel( + systems.toArray( new String[ systems.size() ] ) ) );; + } + + mSystemNameCombo.setSelectedItem( channel.getSystem() ); + + List sites = mChannelModel.getSites( channel.getSystem() ); + + if( sites.isEmpty() ) + { + mSiteNameCombo.setModel( EMPTY_MODEL ); + } + else + { + mSiteNameCombo.setModel( new DefaultComboBoxModel( + sites.toArray( new String[ sites.size() ] ) ) );; + } + + mSiteNameCombo.setSelectedItem( channel.getSite() ); + + String aliasListName = mChannel.getAliasListName(); + + if( aliasListName != null ) + { + AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() + .getAliasList( aliasListName ); + + mAliasListCombo.setSelectedItem( selected ); + } + } + else + { + mChannelName.setText( DEFAULT_NAME ); mSystemNameCombo.setModel( EMPTY_MODEL ); - } - else - { - mSystemNameCombo.setModel( new DefaultComboBoxModel( - systems.toArray( new String[ systems.size() ] ) ) );; - } - - mSystemNameCombo.setSelectedItem( channel.getSystem() ); - - List sites = mChannelModel.getSites( channel.getSystem() ); - - if( sites.isEmpty() ) - { mSiteNameCombo.setModel( EMPTY_MODEL ); + mAliasListCombo.setSelectedIndex( 0 ); } - else - { - mSiteNameCombo.setModel( new DefaultComboBoxModel( - sites.toArray( new String[ sites.size() ] ) ) );; - } - - mSiteNameCombo.setSelectedItem( channel.getSite() ); - - String aliasListName = mChannel.getAliasListName(); - - if( aliasListName != null ) - { - AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mAliasListCombo.setSelectedItem( selected ); - } } } diff --git a/src/controller/channel/OldChannelEditor.java b/src/controller/channel/OldChannelEditor.java index f4f317f8..6882e457 100644 --- a/src/controller/channel/OldChannelEditor.java +++ b/src/controller/channel/OldChannelEditor.java @@ -132,19 +132,19 @@ public class OldChannelEditor extends ChannelConfigurationEditor implements Acti tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); - mSourceEditor = new SourceComponentEditor( mSourceManager, mChannel ); + mSourceEditor = new SourceComponentEditor( mSourceManager ); tabs.addTab( "Source", mSourceEditor ); - mDecodeEditor = new DecodeComponentEditor( mPlaylistManager, mChannel ); + mDecodeEditor = new DecodeComponentEditor( mPlaylistManager ); tabs.addTab( "Decoder", mDecodeEditor ); - mAuxDecodeEditor = new AuxDecodeComponentEditor( mChannel ); + mAuxDecodeEditor = new AuxDecodeComponentEditor(); tabs.addTab( "Aux", mAuxDecodeEditor ); - mEventLogEditor = new EventLogComponentEditor( mChannel ); + mEventLogEditor = new EventLogComponentEditor(); tabs.addTab( "Event Log", mEventLogEditor ); - mRecordEditor = new RecordComponentEditor( mChannel ); + mRecordEditor = new RecordComponentEditor(); tabs.addTab( "Record", mRecordEditor ); add( tabs, "span,grow,push" ); diff --git a/src/module/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeComponentEditor.java index 8e3dc8cb..46ba84fb 100644 --- a/src/module/decode/AuxDecodeComponentEditor.java +++ b/src/module/decode/AuxDecodeComponentEditor.java @@ -17,84 +17,70 @@ ******************************************************************************/ package module.decode; +import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import javax.swing.JCheckBox; -import javax.swing.SwingUtilities; import module.decode.config.AuxDecodeConfiguration; -import controller.channel.AbstractChannelEditor; import controller.channel.Channel; +import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; -public class AuxDecodeComponentEditor extends AbstractChannelEditor +public class AuxDecodeComponentEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; - private HashMap mControls = - new HashMap(); + private List mControls = new ArrayList<>(); - public AuxDecodeComponentEditor( Channel channel ) + private Channel mChannel; + + public AuxDecodeComponentEditor() { - super( channel ); - List decoders = DecoderType.getAuxDecoders(); Collections.sort( decoders ); - + for( DecoderType decoder: decoders ) { AuxDecoderCheckBox control = new AuxDecoderCheckBox( decoder ); - if( getConfig().getAuxDecoders().contains( decoder ) ) - { - control.setSelected( true ); - } - add( control, "wrap" ); - mControls.put( decoder, control ); + mControls.add( control ); } } public AuxDecodeConfiguration getConfig() { - return getChannel().getAuxDecodeConfiguration(); + if( mChannel != null ) + { + return mChannel.getAuxDecodeConfiguration(); + } + + return new AuxDecodeConfiguration(); } @Override public void save() { - getConfig().clearAuxDecoders(); - - for( DecoderType decoder: mControls.keySet() ) + if( mChannel != null ) { - if( mControls.get( decoder ) .isSelected() ) + AuxDecodeConfiguration config = mChannel.getAuxDecodeConfiguration(); + + config.clearAuxDecoders(); + + for( AuxDecoderCheckBox checkBox: mControls ) { - getConfig().addAuxDecoder( decoder ); + if( checkBox.isSelected() ) + { + config.addAuxDecoder( checkBox.getDecoderType() ); + } } } } - @Override - public void reset() - { - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - for( DecoderType decoder: mControls.keySet() ) - { - mControls.get( decoder ).setSelected( - getConfig().getAuxDecoders().contains( decoder ) ); - } - } - }); - } - public class AuxDecoderCheckBox extends JCheckBox { private static final long serialVersionUID = 1L; @@ -107,19 +93,35 @@ public class AuxDecodeComponentEditor extends AbstractChannelEditor mDecoderType = decoder; } + + public DecoderType getDecoderType() + { + return mDecoderType; + } } @Override public void setConfiguration( Channel channel ) { - // TODO Auto-generated method stub - + mChannel = channel; + + for( AuxDecoderCheckBox checkBox: mControls ) + { + if( mChannel != null && + mChannel.getAuxDecodeConfiguration().getAuxDecoders() + .contains( checkBox.getDecoderType() ) ) + { + checkBox.setSelected( true ); + } + else + { + checkBox.setSelected( false ); + } + } } @Override public void validateConfiguration() throws ConfigurationValidationException { - // TODO Auto-generated method stub - } } diff --git a/src/module/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java index bdc48706..bd631d3b 100644 --- a/src/module/decode/DecodeComponentEditor.java +++ b/src/module/decode/DecodeComponentEditor.java @@ -1,5 +1,4 @@ /******************************************************************************* - * SDR Trunk * Copyright (C) 2014-2016 Dennis Sheirer * @@ -24,43 +23,38 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.SwingUtilities; import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import playlist.PlaylistManager; -import controller.channel.AbstractChannelEditor; import controller.channel.Channel; import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; -public class DecodeComponentEditor extends AbstractChannelEditor +public class DecodeComponentEditor extends ChannelConfigurationEditor { - private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( DecodeComponentEditor.class ); + private static final long serialVersionUID = 1L; + + private PlaylistManager mPlaylistManager; private JComboBox mComboDecoders; private DecodeEditor mEditor; - - private PlaylistManager mPlaylistManager; + + private Channel mChannel; public DecodeComponentEditor( PlaylistManager playlistManager ) - { - this( playlistManager, new Channel() ); - } - - public DecodeComponentEditor( PlaylistManager playlistMangager, Channel channel ) { - super( channel ); + mPlaylistManager = playlistManager; - mPlaylistManager = playlistMangager; - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - /** - * ComboBox: Decoders - */ mComboDecoders = new JComboBox(); DefaultComboBoxModel model = @@ -84,14 +78,15 @@ public class DecodeComponentEditor extends AbstractChannelEditor if( selected != null ) { DecodeConfiguration config; - - if( mChannel.getDecodeConfiguration().getDecoderType() == selected ) + + if( mChannel == null || + mChannel.getDecodeConfiguration().getDecoderType() != selected ) { - config = mChannel.getDecodeConfiguration(); + config = DecodeConfigFactory.getDecodeConfiguration( selected ); } else { - config = DecodeConfigFactory.getDecodeConfiguration( selected ); + config = mChannel.getDecodeConfiguration(); } //Remove the existing one @@ -111,29 +106,11 @@ public class DecodeComponentEditor extends AbstractChannelEditor } } }); + add( new JLabel( "Decoder:" ) ); add( mComboDecoders, "wrap" ); - - reset(); } - public void reset() - { - final DecoderType decoder = mChannel.getDecodeConfiguration().getDecoderType(); - - SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() { - mComboDecoders.setSelectedItem( decoder ); - - mComboDecoders.requestFocus(); - - mComboDecoders.requestFocusInWindow(); - } - }); - } - public DecodeConfiguration getDecodeConfig() { return mEditor.getConfig(); @@ -147,7 +124,6 @@ public class DecodeComponentEditor extends AbstractChannelEditor mEditor.save(); } - //The component that calls save will invoke the change broadcast mChannel.setDecodeConfiguration( mEditor.getConfig() ); } @@ -165,14 +141,23 @@ public class DecodeComponentEditor extends AbstractChannelEditor @Override public void setConfiguration( Channel channel ) { - // TODO Auto-generated method stub - + mChannel = channel; + + if( mChannel != null ) + { + final DecoderType decoder = mChannel.getDecodeConfiguration().getDecoderType(); + + mComboDecoders.setSelectedItem( decoder ); + + mComboDecoders.requestFocus(); + + mComboDecoders.requestFocusInWindow(); + } } @Override public void validateConfiguration() throws ConfigurationValidationException { // TODO Auto-generated method stub - } } diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java index 4e626964..023d6c52 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelStatePanel.java @@ -106,18 +106,25 @@ public class ChannelStatePanel extends JPanel ProcessingChain processingChain = channelProcessingManager.getProcessingChain( mChannel ); - mChannelState = processingChain.getChannelState(); - - mChannelState.setChangedAttributeListener( this ); - - init(); + if( processingChain != null ) + { + mChannelState = processingChain.getChannelState(); + + mChannelState.setChangedAttributeListener( this ); + + init(); - for( DecoderState decoderState: processingChain.getDecoderStates() ) - { - DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoderState ); - mDecoderPanels.add( panel ); - add( panel, "grow,span" ); - } + for( DecoderState decoderState: processingChain.getDecoderStates() ) + { + DecoderPanel panel = DecoderFactory.getDecoderPanel( mSettingsManager, decoderState ); + mDecoderPanels.add( panel ); + add( panel, "grow,span" ); + } + } + else + { + mLog.error( "Processing Chain was null!" ); + } } private void init() diff --git a/src/module/log/EventLogComponentEditor.java b/src/module/log/EventLogComponentEditor.java index 2a9e081a..f746b83d 100644 --- a/src/module/log/EventLogComponentEditor.java +++ b/src/module/log/EventLogComponentEditor.java @@ -20,92 +20,79 @@ package module.log; import java.util.List; import javax.swing.JCheckBox; -import javax.swing.SwingUtilities; import module.log.config.EventLogConfiguration; -import controller.channel.AbstractChannelEditor; import controller.channel.Channel; +import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; -public class EventLogComponentEditor extends AbstractChannelEditor +public class EventLogComponentEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; private JCheckBox mBinaryLogger = new JCheckBox( "Binary Messages" ); private JCheckBox mDecodedLogger = new JCheckBox( "Decoded Messages" ); private JCheckBox mCallEventLogger = new JCheckBox( "Call Events" ); + + private Channel mChannel; - public EventLogComponentEditor( Channel channel ) + public EventLogComponentEditor() { - super( channel ); - add( mBinaryLogger, "span" ); add( mDecodedLogger, "span" ); add( mCallEventLogger, "span" ); - - reset(); } - /** - * Sets the combo box to the type stored in the config object, causing the - * editor panel to reset with the stored config - */ - public void reset() - { - SwingUtilities.invokeLater(new Runnable() - { - final List mLoggers = mChannel - .getEventLogConfiguration().getLoggers(); - - @Override - public void run() - { - mBinaryLogger.setSelected( - mLoggers.contains( EventLogType.BINARY_MESSAGE ) ); - mDecodedLogger.setSelected( - mLoggers.contains( EventLogType.DECODED_MESSAGE ) ); - mCallEventLogger.setSelected( - mLoggers.contains( EventLogType.CALL_EVENT ) ); - } - }); - } - /** * Saves the current configuration as the stored configuration */ @Override public void save() { - EventLogConfiguration config = mChannel.getEventLogConfiguration(); + if( mChannel != null ) + { + EventLogConfiguration config = mChannel.getEventLogConfiguration(); - config.clear(); - - if( mBinaryLogger.isSelected() ) - { - config.addLogger( EventLogType.BINARY_MESSAGE ); - } - - if( mDecodedLogger.isSelected() ) - { - config.addLogger( EventLogType.DECODED_MESSAGE ); - } - if( mCallEventLogger.isSelected() ) - { - config.addLogger( EventLogType.CALL_EVENT ); + config.clear(); + + if( mBinaryLogger.isSelected() ) + { + config.addLogger( EventLogType.BINARY_MESSAGE ); + } + if( mDecodedLogger.isSelected() ) + { + config.addLogger( EventLogType.DECODED_MESSAGE ); + } + if( mCallEventLogger.isSelected() ) + { + config.addLogger( EventLogType.CALL_EVENT ); + } } } @Override public void setConfiguration( Channel channel ) { - // TODO Auto-generated method stub - + mChannel = channel; + + if( mChannel != null ) + { + List loggers = mChannel.getEventLogConfiguration().getLoggers(); + + mBinaryLogger.setSelected( loggers.contains( EventLogType.BINARY_MESSAGE ) ); + mDecodedLogger.setSelected( loggers.contains( EventLogType.DECODED_MESSAGE ) ); + mCallEventLogger.setSelected( loggers.contains( EventLogType.CALL_EVENT ) ); + } + else + { + mBinaryLogger.setSelected( false ); + mDecodedLogger.setSelected( false ); + mCallEventLogger.setSelected( false ); + } } @Override public void validateConfiguration() throws ConfigurationValidationException { - // TODO Auto-generated method stub - } } diff --git a/src/playlist/PlaylistEditor.java b/src/playlist/PlaylistEditor.java index 5d5d92a2..e1a69901 100644 --- a/src/playlist/PlaylistEditor.java +++ b/src/playlist/PlaylistEditor.java @@ -54,13 +54,15 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener mChannelTable.getSelectionModel().addListSelectionListener( this ); mChannelTable.setAutoCreateRowSorter( true ); - TableFilterHeader channelHeaderFilter = new TableFilterHeader( mChannelTable ); - channelHeaderFilter.setAutoChoices( AutoChoices.ENABLED ); + TableFilterHeader channelHeaderFilter = + new TableFilterHeader( mChannelTable, AutoChoices.ENABLED ); + channelHeaderFilter.setFilterOnUpdates( true ); JScrollPane channelScroller = new JScrollPane( mChannelTable ); JideSplitPane splitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); splitPane.setDividerSize( 5 ); + splitPane.setShowGripper( true ); splitPane.add( channelScroller ); splitPane.add( mEditor ); @@ -73,15 +75,22 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener //This limits event firing to only when selection is complete if( !event.getValueIsAdjusting() ) { - int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); - - mLog.debug( "Index:" + index ); + int selectedRow = mChannelTable.getSelectedRow(); - Channel channel = mChannelModel.getChannels().get( index ); - - if( channel != null ) + if( selectedRow != -1 ) { - mEditor.setChannel( channel ); + int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); + + Channel channel = mChannelModel.getChannels().get( index ); + + if( channel != null ) + { + mEditor.setChannel( channel ); + } + } + else + { + mEditor.setChannel( null ); } } } diff --git a/src/record/RecordComponentEditor.java b/src/record/RecordComponentEditor.java index 7c23a360..5873534d 100644 --- a/src/record/RecordComponentEditor.java +++ b/src/record/RecordComponentEditor.java @@ -17,15 +17,16 @@ ******************************************************************************/ package record; +import java.util.List; import javax.swing.JCheckBox; import record.config.RecordConfiguration; -import controller.channel.AbstractChannelEditor; import controller.channel.Channel; +import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; -public class RecordComponentEditor extends AbstractChannelEditor +public class RecordComponentEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; @@ -33,74 +34,63 @@ public class RecordComponentEditor extends AbstractChannelEditor private JCheckBox mBasebandRecorder = new JCheckBox( "Baseband I/Q" ); private JCheckBox mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" ); - public RecordComponentEditor( Channel channel ) + private Channel mChannel; + + public RecordComponentEditor() { - super( channel ); - add( mAudioRecorder, "span" ); add( mBasebandRecorder, "span" ); add( mTrafficBasebandRecorder, "span" ); - - reset(); } @Override public void save() { - RecordConfiguration config = getChannel().getRecordConfiguration(); - - config.clearRecorders(); - - if( mAudioRecorder.isSelected() ) + if( mChannel != null ) { - config.addRecorder( RecorderType.AUDIO ); + RecordConfiguration config = mChannel.getRecordConfiguration(); + + config.clearRecorders(); + + if( mAudioRecorder.isSelected() ) + { + config.addRecorder( RecorderType.AUDIO ); + } + + if( mBasebandRecorder.isSelected() ) + { + config.addRecorder( RecorderType.BASEBAND ); + } + + if( mTrafficBasebandRecorder.isSelected() ) + { + config.addRecorder( RecorderType.TRAFFIC_BASEBAND ); + } } - - if( mBasebandRecorder.isSelected() ) - { - config.addRecorder( RecorderType.BASEBAND ); - } - - if( mTrafficBasebandRecorder.isSelected() ) - { - config.addRecorder( RecorderType.TRAFFIC_BASEBAND ); - } - } - - @Override - public void reset() - { - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - RecordConfiguration config = getChannel().getRecordConfiguration(); - - mAudioRecorder.setSelected( config.getRecorders() - .contains( RecorderType.AUDIO ) ); - - mBasebandRecorder.setSelected( config.getRecorders() - .contains( RecorderType.BASEBAND ) ); - - mTrafficBasebandRecorder.setSelected( config.getRecorders() - .contains( RecorderType.TRAFFIC_BASEBAND ) ); - } - }); - } - @Override public void setConfiguration( Channel channel ) { - // TODO Auto-generated method stub + mChannel = channel; + if( mChannel != null ) + { + List recorders = mChannel.getRecordConfiguration().getRecorders(); + + mAudioRecorder.setSelected( recorders.contains( RecorderType.AUDIO ) ); + mBasebandRecorder.setSelected( recorders.contains( RecorderType.BASEBAND ) ); + mTrafficBasebandRecorder.setSelected( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) ); + } + else + { + mAudioRecorder.setSelected( false ); + mBasebandRecorder.setSelected( false ); + mTrafficBasebandRecorder.setSelected( false ); + } } @Override public void validateConfiguration() throws ConfigurationValidationException { - // TODO Auto-generated method stub - } } diff --git a/src/record/config/RecordConfiguration.java b/src/record/config/RecordConfiguration.java index 93c32cf9..5315e878 100644 --- a/src/record/config/RecordConfiguration.java +++ b/src/record/config/RecordConfiguration.java @@ -18,6 +18,7 @@ package record.config; import java.util.ArrayList; +import java.util.List; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; @@ -28,19 +29,19 @@ import controller.config.Configuration; @XmlRootElement( name = "record_configuration" ) public class RecordConfiguration extends Configuration { - private ArrayList mRecorders = new ArrayList(); + private List mRecorders = new ArrayList<>(); public RecordConfiguration() { } @XmlElement( name = "recorder" ) - public ArrayList getRecorders() + public List getRecorders() { return mRecorders; } - public void setRecorders( ArrayList recorders ) + public void setRecorders( List recorders ) { mRecorders = recorders; } diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java index b4f3673a..faa5c78c 100644 --- a/src/source/SourceComponentEditor.java +++ b/src/source/SourceComponentEditor.java @@ -28,22 +28,21 @@ import javax.swing.JLabel; import net.miginfocom.swing.MigLayout; import source.config.SourceConfigFactory; import source.config.SourceConfiguration; -import controller.channel.AbstractChannelEditor; import controller.channel.Channel; +import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; -public class SourceComponentEditor extends AbstractChannelEditor +public class SourceComponentEditor extends ChannelConfigurationEditor { private static final long serialVersionUID = 1L; private JComboBox mComboSources; private SourceEditor mEditor; private SourceManager mSourceManager; + private Channel mChannel = null; - public SourceComponentEditor( SourceManager sourceManager, Channel channel ) + public SourceComponentEditor( SourceManager sourceManager ) { - super( channel ); - mSourceManager = sourceManager; setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); @@ -60,15 +59,15 @@ public class SourceComponentEditor extends AbstractChannelEditor if( selected != null ) { SourceConfiguration config; - - if( selected == mChannel.getSourceConfiguration().getSourceType() ) + + if( mChannel == null || + mChannel.getSourceConfiguration().getSourceType() != selected ) { - config = mChannel.getSourceConfiguration(); + config = SourceConfigFactory.getSourceConfiguration( selected ); } else { - config = SourceConfigFactory - .getSourceConfiguration( selected ); + config = mChannel.getSourceConfiguration(); } //Remove the existing one @@ -91,8 +90,6 @@ public class SourceComponentEditor extends AbstractChannelEditor add( new JLabel( "Source:" ) ); add( mComboSources, "wrap" ); - - reset(); } public SourceEditor getSourceEditor() @@ -100,24 +97,6 @@ public class SourceComponentEditor extends AbstractChannelEditor return mEditor; } - public void reset() - { - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - - @Override - public void run() - { - mComboSources.setSelectedItem( - mChannel.getSourceConfiguration().getSourceType() ); - - mComboSources.requestFocus(); - - mComboSources.requestFocusInWindow(); - } - }); - } - @Override public void save() { @@ -127,17 +106,29 @@ public class SourceComponentEditor extends AbstractChannelEditor mChannel.setSourceConfiguration( mEditor.getConfig() ); } + /** + * Sets the channel configuration. Note: this method must be invoked from + * the swing event dispatch thread. + */ @Override public void setConfiguration( Channel channel ) { - // TODO Auto-generated method stub - + mChannel = channel; + + if( mChannel != null ) + { + mComboSources.setSelectedItem( + mChannel.getSourceConfiguration().getSourceType() ); + + mComboSources.requestFocus(); + + mComboSources.requestFocusInWindow(); + } } @Override public void validateConfiguration() throws ConfigurationValidationException { - // TODO Auto-generated method stub } } From ec10bb7e625a0063a6b8ef0e895ae3f723199b66 Mon Sep 17 00:00:00 2001 From: denny Date: Wed, 13 Jan 2016 08:59:34 -0500 Subject: [PATCH 04/74] Work in progress. --- src/controller/channel/ChannelEditor.java | 35 +++++++++++++------ .../channel/ChannelProcessingManager.java | 8 ++--- src/playlist/PlaylistEditor.java | 1 + 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 99387271..6df2eb4e 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -30,6 +30,7 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JSeparator; import javax.swing.JTextField; import module.decode.AuxDecodeComponentEditor; @@ -66,7 +67,8 @@ public class ChannelEditor extends JPanel private Channel mChannel; - private JCheckBox mChannelEnabled = new JCheckBox(); + private JButton mEnableButton = new JButton( "Enable" ); + private JLabel mChannelName = new JLabel( "Channel:" ); private ChannelModel mChannelModel; private PlaylistManager mPlaylistManager; @@ -101,6 +103,22 @@ public class ChannelEditor extends JPanel mEventLogEditor.setConfiguration( channel ); mRecordEditor.setConfiguration( channel ); + if( channel != null ) + { + mChannelName.setText( "Channel: " + channel.getName() ); + mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" ); + mEnableButton.setEnabled( true ); + mEnableButton.setBackground( channel.getEnabled() ? Color.RED : Color.GREEN ); + } + else + { + mChannelName.setText( "Channel: " ); + mEnableButton.setText( "Enable" ); + mEnableButton.setEnabled( false ); + mEnableButton.setBackground( getBackground() ); + } + + mChannel = channel; } @@ -108,12 +126,11 @@ public class ChannelEditor extends JPanel { setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) ); - add( new JLabel( "Channel Configuration" ), "span, align center" ); - - add( new JLabel( "Enabled:" ), "wrap" ); - - add( mNameConfigurationEditor, "growx" ); + mEnableButton.setEnabled( false ); + add( mEnableButton, "growx,push" ); + add( mChannelName, "growx,push" ); + add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" ); JideTabbedPane tabs = new JideTabbedPane(); tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); @@ -149,11 +166,7 @@ public class ChannelEditor extends JPanel @Override public void channelChanged( ChannelEvent event ) { - if( !EventQueue.isDispatchThread() ) - { - mLog.debug( "Not on the swing thread!!!!" ); - } - + mLog.debug( "Received channel event: " + event.getEvent() ); if( mChannel != null && mChannel == event.getChannel() ) { switch( event.getEvent() ) diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index 7c367040..5d589766 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -263,14 +263,16 @@ public class ChannelProcessingManager implements ChannelEventListener private void stopProcessing( Channel channel, boolean remove ) { + channel.setEnabled( false ); + if( mProcessingChains.containsKey( channel.getChannelID() ) ) { ProcessingChain chain = mProcessingChains.get( channel.getChannelID() ); chain.stop(); - mChannelModel.broadcast( - new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) ); + mChannelModel.broadcast( new ChannelEvent( channel, + Event.NOTIFICATION_PROCESSING_STOP ) ); if( remove ) { @@ -279,8 +281,6 @@ public class ChannelProcessingManager implements ChannelEventListener chain.dispose(); } } - - channel.setEnabled( false ); } /** diff --git a/src/playlist/PlaylistEditor.java b/src/playlist/PlaylistEditor.java index e1a69901..d94a9c6f 100644 --- a/src/playlist/PlaylistEditor.java +++ b/src/playlist/PlaylistEditor.java @@ -38,6 +38,7 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener mChannelModel = channelModel; mEditor = new ChannelEditor( channelModel, playlistManager, sourceManager ); + mChannelModel.addListener( mEditor ); init(); } From 61f68c08e8ea1560cf6bdb5ece2371a57b73ffec Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 24 Jan 2016 07:19:30 -0500 Subject: [PATCH 05/74] Adds support to channel model and processing manager to auto-start channels upon loading the playlist. Processing manager now detects channel configuration changes and restarts the channel if it is currently processing. Channel editor enable button now shows green if it is processing, or default when not processing. --- src/controller/channel/ChannelEditor.java | 35 +++- src/controller/channel/ChannelModel.java | 5 + .../channel/ChannelProcessingManager.java | 7 + src/gui/SDRTrunk.java | 7 +- src/instrument/gui/DecoderSelectionFrame.java | 6 +- src/playlist/PlaylistManager.java | 160 +++++++++++++++++- 6 files changed, 210 insertions(+), 10 deletions(-) diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 6df2eb4e..7879b03d 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -74,6 +74,8 @@ public class ChannelEditor extends JPanel private PlaylistManager mPlaylistManager; private SourceManager mSourceManager; + private boolean mChannelEnableRequested = false; + public ChannelEditor( ChannelModel channelModel, PlaylistManager playlistManager, SourceManager sourceManager ) @@ -108,7 +110,7 @@ public class ChannelEditor extends JPanel mChannelName.setText( "Channel: " + channel.getName() ); mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" ); mEnableButton.setEnabled( true ); - mEnableButton.setBackground( channel.getEnabled() ? Color.RED : Color.GREEN ); + mEnableButton.setBackground( channel.getEnabled() ? Color.GREEN : getBackground() ); } else { @@ -126,6 +128,7 @@ public class ChannelEditor extends JPanel { setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) ); + mEnableButton.addActionListener( this ); mEnableButton.setEnabled( false ); add( mEnableButton, "growx,push" ); add( mChannelName, "growx,push" ); @@ -175,15 +178,28 @@ public class ChannelEditor extends JPanel case NOTIFICATION_PROCESSING_START: case NOTIFICATION_PROCESSING_STOP: setChannel( mChannel ); + mChannelEnableRequested = false; break; case NOTIFICATION_DELETE: mChannel = null; setChannel( mChannel ); break; + case NOTIFICATION_ENABLE_REJECTED: + if( mChannelEnableRequested ) + { + JOptionPane.showMessageDialog( ChannelEditor.this, + "Channel could not be enabled. This is likely because " + + "there are no tuner channels available", "Couldn't " + + "enable channel", JOptionPane.INFORMATION_MESSAGE ); + + mChannelEnableRequested = false; + } + break; default: break; } } + } @Override @@ -191,7 +207,22 @@ public class ChannelEditor extends JPanel { String command = e.getActionCommand(); - if( command.contentEquals( "Save" ) ) + if( command.contentEquals( "Enable" ) ) + { + if( mChannel != null ) + { + mChannelEnableRequested = true; + mChannelModel.broadcast( new ChannelEvent( mChannel, Event.REQUEST_ENABLE ) ); + } + } + else if( command.contentEquals( "Disable" ) ) + { + if( mChannel != null ) + { + mChannelModel.broadcast( new ChannelEvent( mChannel, Event.REQUEST_DISABLE ) ); + } + } + else if( command.contentEquals( "Save" ) ) { save(); } diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java index 1f0d6821..751c374e 100644 --- a/src/controller/channel/ChannelModel.java +++ b/src/controller/channel/ChannelModel.java @@ -170,6 +170,11 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel } broadcast( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) ); + + if( channel.getEnabled() ) + { + broadcast( new ChannelEvent( channel, Event.REQUEST_ENABLE ) ); + } } /** diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index 5d589766..610c4437 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -140,6 +140,13 @@ public class ChannelProcessingManager implements ChannelEventListener stopProcessing( channel, true ); } break; + case NOTIFICATION_CONFIGURATION_CHANGE: + if( isProcessing( channel ) ) + { + stopProcessing( channel, false ); + startProcessing( channel ); + } + break; default: break; } diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 6de05d02..4447562f 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -120,7 +120,7 @@ public class SDRTrunk ChannelModel channelModel = new ChannelModel(); - PlaylistManager playlistManager = new PlaylistManager( channelModel ); + PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, channelModel ); RecorderManager recorderManager = new RecorderManager( threadPoolManager ); @@ -146,6 +146,9 @@ public class SDRTrunk new ChannelSelectionManager( channelModel ); channelModel.addListener( channelSelectionManager ); +//TODO: move this to the end once we add the alias model, otherwise the +//configuration tree won't load correctly + //Initialize the playlist manager to load the saved playlist playlistManager.init(); diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index 998191b1..8c242b66 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -13,6 +13,7 @@ import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; import source.IControllableFileSource; +import controller.ThreadPoolManager; import controller.channel.ChannelModel; public class DecoderSelectionFrame extends JInternalFrame @@ -29,7 +30,10 @@ public class DecoderSelectionFrame extends JInternalFrame IControllableFileSource source ) { ChannelModel channelModel = new ChannelModel(); - mPlaylistManager = new PlaylistManager( channelModel ); + + ThreadPoolManager tpm = new ThreadPoolManager(); + + mPlaylistManager = new PlaylistManager( tpm, channelModel ); mDesktop = desktop; mSource = source; diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index 9d43c496..dea60a8c 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -25,6 +25,8 @@ import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; @@ -39,8 +41,16 @@ import controller.channel.Channel; import controller.channel.ChannelModel; import controller.site.Site; import controller.system.SystemList; +import controller.ThreadPoolManager; +import controller.channel.Channel; +import controller.channel.Channel.ChannelType; +import controller.channel.ChannelEvent; +import controller.channel.ChannelEventListener; +import controller.channel.ChannelModel; +import controller.site.Site; +import controller.system.SystemList; -public class PlaylistManager +public class PlaylistManager implements ChannelEventListener { private final static Logger mLog = LoggerFactory.getLogger( PlaylistManager.class ); @@ -50,8 +60,31 @@ public class PlaylistManager private ChannelModel mChannelModel; public PlaylistManager( ChannelModel channelModel ) + private ThreadPoolManager mThreadPoolManager; + private ChannelModel mChannelModel; + + private AtomicBoolean mPlaylistSavePending = new AtomicBoolean(); + private boolean mPlaylistLoading = false; + + /** + * Playlist manager - manages all channel configurationc, channel maps, and + * alias lists and handles loading or persisting to a playlist.xml file + * + * Monitors playlist changes to automatically save configuration changes + * after they occur. + * + * @param threadPoolManager + * @param channelModel + */ + public PlaylistManager( ThreadPoolManager threadPoolManager, + ChannelModel channelModel ) { + mThreadPoolManager = threadPoolManager; mChannelModel = channelModel; + + //Register for channel events so that we can save the playlist when + //any channels change + mChannelModel.addListener( this ); } public Playlist getPlayist() @@ -64,8 +97,11 @@ public class PlaylistManager */ private void transferPlaylistToModels() { - //Load channels + mPlaylistLoading = true; + mChannelModel.addChannels( mPlaylist.getChannels() ); + + mPlaylistLoading = false; } /** @@ -88,7 +124,46 @@ public class PlaylistManager transferPlaylistToModels(); } - + + /** + * Channel event listener method. Monitors channel events for events that + * indicate that the playlist has changed and queues automatic playlist + * saving. + */ + @Override + public void channelChanged( ChannelEvent event ) + { + //Only save playlist for changes to standard channels (not traffic) + if( event.getChannel().getChannelType() == ChannelType.STANDARD ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_ADD: + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_DELETE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + schedulePlaylistSave(); + break; + case NOTIFICATION_ENABLE_REJECTED: + case NOTIFICATION_SELECTION_CHANGE: + case NOTIFICATION_STATE_RESET: + case REQUEST_DELETE: + case REQUEST_DESELECT: + case REQUEST_DISABLE: + case REQUEST_ENABLE: + case REQUEST_SELECT: + //Do nothing for these event types + break; + default: + //When a new event enum entry is added and received, throw an + //exception here to ensure developer adds support for the + //use case + throw new IllegalArgumentException( "Unrecognized Channel " + + "Event [" + event.getEvent().name() + "]" ); + } + } + } public void save() { @@ -284,12 +359,87 @@ public class PlaylistManager } else { - mLog.info( "playlist does not exist [" + playlistPath.toString() + "]" ); + mLog.info( "PlaylistManager - playlist not found at [" + + playlistPath.toString() + "]" ); } if( mPlaylist == null ) { mPlaylist = new Playlist(); + save(); + } + + //Check for and convert from legacy play list format + if( mPlaylist.hasSystemList() ) + { + convertPlaylistFormat(); + } + } + + /** + * Converts playlist data over to new format + */ + private void convertPlaylistFormat() + { + mLog.info( "Legacy playlist format detected - converting ..." ); + + createBackupPlaylist(); + + //Playlist version 1 to version 2 format conversion. In order to keep + //backwards compatibility, transfer all of the System-Site-Channel + //objects over to the new channel list format + SystemList systemList = mPlaylist.getSystemList(); + + for( controller.system.System system: systemList.getSystem() ) + { + for( Site site: system.getSite() ) + { + for( Channel channel: site.getChannel() ) + { + channel.setSystem( system.getName() ); + channel.setSite( site.getName() ); + + mPlaylist.getChannels().add( channel ); + } + } + } + + mPlaylist.getSystemList().clearSystems(); + + mLog.info( "Converted [" + mPlaylist.getChannels().size() + + "] channels to new playlist format" ); + + save(); + } + + /** + * Schedules a playlist save task. Subsequent calls to this method will be + * ignored until the save event occurs, thus limiting repetitive playlist + * saving to a minimum. + */ + private void schedulePlaylistSave() + { + if( !mPlaylistLoading ) + { + if( mPlaylistSavePending.compareAndSet( false, true ) ) + { + mThreadPoolManager.scheduleOnce( new PlaylistSaveTask(), + 2, TimeUnit.SECONDS ); + } + } + } + + /** + * Saves the playlist and resets the playlist save pending flag to false + */ + public class PlaylistSaveTask implements Runnable + { + @Override + public void run() + { + save(); + + mPlaylistSavePending.set( false ); } //Check for and convert from legacy play list format From da924e8b3aae886ddef28c09dc092feadcfdd446 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 24 Jan 2016 10:31:31 -0500 Subject: [PATCH 06/74] Updates playlist editor with Add, Copy/Clone and Delete channel functionality --- src/controller/channel/Channel.java | 47 ++++++ src/controller/channel/ChannelEditor.java | 37 ----- src/controller/channel/ChannelModel.java | 64 ++++++--- .../decode/config/DecodeConfigFactory.java | 79 +++++++++- src/playlist/PlaylistEditor.java | 136 +++++++++++++++++- src/playlist/PlaylistManager.java | 7 + src/source/config/SourceConfigFactory.java | 35 +++++ 7 files changed, 337 insertions(+), 68 deletions(-) diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index b7d290a8..a86d4aae 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -23,14 +23,17 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; +import module.decode.DecoderType; import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; +import module.log.EventLogType; import module.log.config.EventLogConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import record.RecorderType; import record.config.RecordConfiguration; import source.SourceType; import source.config.SourceConfigFactory; @@ -115,6 +118,50 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor { mChannelID = UNIQUE_ID++; } + + /** + * Creates a (new) deep copy of this channel + */ + public Channel copyOf() + { + Channel channel = new Channel( mName ); + channel.setSystem( mSystem ); + channel.setSite( mSite ); + channel.setAliasListName( mAliasListName ); + + AuxDecodeConfiguration aux = new AuxDecodeConfiguration(); + + for( DecoderType auxType: aux.getAuxDecoders() ) + { + aux.addAuxDecoder( auxType ); + } + + channel.setAuxDecodeConfiguration( aux ); + + channel.setDecodeConfiguration( DecodeConfigFactory.copy( mDecodeConfiguration ) ); + + EventLogConfiguration log = new EventLogConfiguration(); + + for( EventLogType logType: mEventLogConfiguration.getLoggers() ) + { + log.addLogger( logType ); + } + + channel.setEventLogConfiguration( log ); + + RecordConfiguration record = new RecordConfiguration(); + + for( RecorderType recordType: mRecordConfiguration.getRecorders() ) + { + record.addRecorder( recordType ); + } + + channel.setRecordConfiguration( record ); + + channel.setSourceConfiguration( SourceConfigFactory.copy( mSourceConfiguration ) ); + + return channel; + } //TODO: remove this /** diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 7879b03d..4e6c85e9 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -17,21 +17,16 @@ ******************************************************************************/ package controller.channel; - import java.awt.Color; import java.awt.Component; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JSeparator; -import javax.swing.JTextField; import module.decode.AuxDecodeComponentEditor; import module.decode.DecodeComponentEditor; @@ -45,7 +40,6 @@ import playlist.PlaylistManager; import record.RecordComponentEditor; import source.SourceComponentEditor; import source.SourceManager; -import alias.AliasList; import com.jidesoft.swing.JideTabbedPane; @@ -169,7 +163,6 @@ public class ChannelEditor extends JPanel @Override public void channelChanged( ChannelEvent event ) { - mLog.debug( "Received channel event: " + event.getEvent() ); if( mChannel != null && mChannel == event.getChannel() ) { switch( event.getEvent() ) @@ -302,36 +295,6 @@ public class ChannelEditor extends JPanel mChannelModel.broadcast( new ChannelEvent( mChannel, Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); } - -// @Override -// public void reset() -// { -// mChannelName.setText( mChannel.getName() ); -// mChannelName.requestFocus(); -// mChannelName.requestFocusInWindow(); -// -// mChannelEnabled.setSelected( mChannel.getEnabled() ); -// mChannelEnabled.requestFocus(); -// mChannelEnabled.requestFocusInWindow(); -// -// String aliasListName = mChannel.getAliasListName(); -// -// if( aliasListName != null ) -// { -// AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() -// .getAliasList( aliasListName ); -// -// mComboAliasLists.setSelectedItem( selected ); -// } -// -// -// mSourceEditor.reset(); -// mDecodeEditor.reset(); -// mAuxDecodeEditor.reset(); -// mEventLogEditor.reset(); -// mRecordEditor.reset(); -// } - // /** // * Validate the specified editor against each of the component editors // */ diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java index 751c374e..ab1e3a88 100644 --- a/src/controller/channel/ChannelModel.java +++ b/src/controller/channel/ChannelModel.java @@ -26,6 +26,10 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistEditor; import module.decode.DecoderType; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent.Event; @@ -35,6 +39,8 @@ import controller.channel.ChannelEvent.Event; */ public class ChannelModel implements IChannelEventBroadcaster, TableModel { + private final static Logger mLog = LoggerFactory.getLogger( ChannelModel.class ); + private ChannelSelectionManager mChannelSelectionManager; private List mChannels = new ArrayList<>(); private List mTrafficChannels = new ArrayList<>(); @@ -55,6 +61,16 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel return Collections.unmodifiableList( mChannels ); } + public Channel getChannelAtIndex( int row ) + { + if( mChannels.size() >= row ) + { + return mChannels.get( row ); + } + + return null; + } + /** * Returns a list of unique system values from across the channel set */ @@ -150,20 +166,23 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel /** * Adds the channel to the model and broadcasts a channel add event */ - public void addChannel( Channel channel ) + public int addChannel( Channel channel ) { + int index = -1; + switch( channel.getChannelType() ) { case STANDARD: mChannels.add( channel ); - int index = mChannels.size() - 1; + index = mChannels.size() - 1; broadcast( new TableModelEvent( this, index, index, TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT ) ); break; case TRAFFIC: mTrafficChannels.add( channel ); + index = mChannels.size() - 1; break; default: break; @@ -175,6 +194,8 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel { broadcast( new ChannelEvent( channel, Event.REQUEST_ENABLE ) ); } + + return index; } /** @@ -182,27 +203,30 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel */ public void removeChannel( Channel channel ) { - switch( channel.getChannelType() ) + if( channel != null ) { - case STANDARD: - int index = mChannels.indexOf( channel ); - - mChannels.remove( channel ); + switch( channel.getChannelType() ) + { + case STANDARD: + int index = mChannels.indexOf( channel ); + + mChannels.remove( channel ); - if( index >= 0 ) - { - broadcast( new TableModelEvent( this, index, index, - TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); - } - break; - case TRAFFIC: - mTrafficChannels.remove( channel ); - break; - default: - break; + if( index >= 0 ) + { + broadcast( new TableModelEvent( this, index, index, + TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); + } + break; + case TRAFFIC: + mTrafficChannels.remove( channel ); + break; + default: + break; + } + + broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) ); } - - broadcast( new ChannelEvent( channel, Event.NOTIFICATION_DELETE ) ); } /** diff --git a/src/module/decode/config/DecodeConfigFactory.java b/src/module/decode/config/DecodeConfigFactory.java index a4a88398..8c0a0f0d 100644 --- a/src/module/decode/config/DecodeConfigFactory.java +++ b/src/module/decode/config/DecodeConfigFactory.java @@ -43,18 +43,18 @@ public class DecodeConfigFactory case AM: retVal = new DecodeConfigAM(); break; - case NBFM: - retVal = new DecodeConfigNBFM(); + case LTR_NET: + retVal = new DecodeConfigLTRNet(); break; case LTR_STANDARD: retVal = new DecodeConfigLTRStandard(); break; - case LTR_NET: - retVal = new DecodeConfigLTRNet(); - break; case MPT1327: retVal = new DecodeConfigMPT1327(); break; + case NBFM: + retVal = new DecodeConfigNBFM(); + break; case PASSPORT: retVal = new DecodeConfigPassport(); break; @@ -68,4 +68,73 @@ public class DecodeConfigFactory return retVal; } + + /** + * Creates a copy of the configuration + */ + public static DecodeConfiguration copy( DecodeConfiguration config ) + { + if( config != null ) + { + switch( config.getDecoderType() ) + { + case AM: + DecodeConfigAM originalAM = (DecodeConfigAM)config; + DecodeConfigAM copyAM = new DecodeConfigAM(); + copyAM.setAFC( originalAM.getAFC() ); + copyAM.setAFCMaximumCorrection( originalAM.getAFCMaximumCorrection() ); + return copyAM; + case LTR_NET: + DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet)config; + DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet(); + copyLTRNet.setAFC( originalLTRNet.getAFC() ); + copyLTRNet.setAFCMaximumCorrection( originalLTRNet.getAFCMaximumCorrection() ); + copyLTRNet.setMessageDirection( originalLTRNet.getMessageDirection() ); + return copyLTRNet; + case LTR_STANDARD: + DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard)config; + DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard(); + copyLTRStandard.setAFC( originalLTRStandard.getAFC() ); + copyLTRStandard.setAFCMaximumCorrection( originalLTRStandard.getAFCMaximumCorrection() ); + copyLTRStandard.setMessageDirection( originalLTRStandard.getMessageDirection() ); + return copyLTRStandard; + case MPT1327: + DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327)config; + DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327(); + copyMPT.setAFC( originalMPT.getAFC() ); + copyMPT.setAFCMaximumCorrection( originalMPT.getAFCMaximumCorrection() ); + copyMPT.setCallTimeout( originalMPT.getCallTimeout() ); + copyMPT.setChannelMapName( originalMPT.getChannelMapName() ); + copyMPT.setSync( originalMPT.getSync() ); + copyMPT.setTrafficChannelPoolSize( originalMPT.getTrafficChannelPoolSize() ); + return copyMPT; + case NBFM: + DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM)config; + DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM(); + copyNBFM.setAFC( originalNBFM.getAFC() ); + copyNBFM.setAFCMaximumCorrection( originalNBFM.getAFCMaximumCorrection() ); + return copyNBFM; + case P25_PHASE1: + DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1)config; + DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1(); + copyP25.setAFC( originalP25.getAFC() ); + copyP25.setAFCMaximumCorrection( originalP25.getAFCMaximumCorrection() ); + copyP25.setIgnoreDataCalls( originalP25.getIgnoreDataCalls() ); + copyP25.setModulation( originalP25.getModulation() ); + copyP25.setTrafficChannelPoolSize( originalP25.getTrafficChannelPoolSize() ); + return copyP25; + case PASSPORT: + DecodeConfigPassport originalPass = (DecodeConfigPassport)config; + DecodeConfigPassport copyPass = new DecodeConfigPassport(); + copyPass.setAFC( originalPass.getAFC() ); + copyPass.setAFCMaximumCorrection( originalPass.getAFCMaximumCorrection() ); + return copyPass; + default: + throw new IllegalArgumentException( "Unrecognized decoder " + + "configuration type:" + config.getDecoderType() ); + } + } + + return null; + } } diff --git a/src/playlist/PlaylistEditor.java b/src/playlist/PlaylistEditor.java index d94a9c6f..f48212cf 100644 --- a/src/playlist/PlaylistEditor.java +++ b/src/playlist/PlaylistEditor.java @@ -1,5 +1,11 @@ package playlist; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -22,14 +28,24 @@ import controller.channel.Channel; import controller.channel.ChannelEditor; import controller.channel.ChannelModel; -public class PlaylistEditor extends JPanel implements ListSelectionListener +public class PlaylistEditor extends JPanel + implements ActionListener, ListSelectionListener { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( PlaylistEditor.class ); private ChannelModel mChannelModel; private JTable mChannelTable; + private TableFilterHeader mTableFilterHeader; private ChannelEditor mEditor; + + private static final String NEW_CHANNEL = "New"; + private static final String COPY_CHANNEL = "Copy"; + private static final String DELETE_CHANNEL = "Delete"; + + private JButton mNewChannelButton = new JButton( NEW_CHANNEL ); + private JButton mCopyChannelButton = new JButton( COPY_CHANNEL ); + private JButton mDeleteChannelButton = new JButton( DELETE_CHANNEL ); public PlaylistEditor( ChannelModel channelModel, PlaylistManager playlistManager, @@ -55,16 +71,42 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener mChannelTable.getSelectionModel().addListSelectionListener( this ); mChannelTable.setAutoCreateRowSorter( true ); - TableFilterHeader channelHeaderFilter = - new TableFilterHeader( mChannelTable, AutoChoices.ENABLED ); - channelHeaderFilter.setFilterOnUpdates( true ); + mTableFilterHeader = new TableFilterHeader( mChannelTable, AutoChoices.ENABLED ); + mTableFilterHeader.setFilterOnUpdates( true ); JScrollPane channelScroller = new JScrollPane( mChannelTable ); + + JPanel buttonsPanel = new JPanel(); + + buttonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); + + mNewChannelButton.addActionListener( this ); + mNewChannelButton.setToolTipText( "Adds a new default channel" ); + buttonsPanel.add( mNewChannelButton ); + + mCopyChannelButton.addActionListener( this ); + mCopyChannelButton.setEnabled( false ); + mCopyChannelButton.setToolTipText( "Creates a copy of the currently selected channel and adds it" ); + buttonsPanel.add( mCopyChannelButton ); + + mDeleteChannelButton.addActionListener( this ); + mDeleteChannelButton.setEnabled( false ); + mDeleteChannelButton.setToolTipText( "Deletes the currently selected channel" ); + buttonsPanel.add( mDeleteChannelButton ); + + JPanel listAndButtonsPanel = new JPanel(); + + listAndButtonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); + + listAndButtonsPanel.add( channelScroller, "wrap" ); + listAndButtonsPanel.add( buttonsPanel ); JideSplitPane splitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); splitPane.setDividerSize( 5 ); splitPane.setShowGripper( true ); - splitPane.add( channelScroller ); + splitPane.add( listAndButtonsPanel ); splitPane.add( mEditor ); add( splitPane ); @@ -82,17 +124,99 @@ public class PlaylistEditor extends JPanel implements ListSelectionListener { int index = mChannelTable.convertRowIndexToModel( mChannelTable.getSelectedRow() ); - Channel channel = mChannelModel.getChannels().get( index ); + Channel channel = mChannelModel.getChannelAtIndex( index ); if( channel != null ) { mEditor.setChannel( channel ); + mCopyChannelButton.setEnabled( true ); + mDeleteChannelButton.setEnabled( true ); } } else { mEditor.setChannel( null ); + mCopyChannelButton.setEnabled( false ); + mDeleteChannelButton.setEnabled( false ); } } } + + /** + * Selects the channel's table row and scrolls the table's viewport to show it + */ + private void addChannel( Channel channel ) + { + //HACK: when inserting a row to the model, the JTable gets + //notified and attempts to tell the coderazzi table filter + //adaptive choices filter to refresh before the table filter is + //notified of the row additions, causing an index out of bounds + //exception. We turn off adaptive choices temporarily, add the + //channel, and turn on adaptive choices again. + mTableFilterHeader.setAdaptiveChoices( false ); + + int index = mChannelModel.addChannel( channel ); + + mTableFilterHeader.setAdaptiveChoices( true ); + + if( index >= 0 ) + { + int translatedIndex = mChannelTable.convertRowIndexToView( index ); + mChannelTable.setRowSelectionInterval( translatedIndex, translatedIndex ); + mChannelTable.scrollRectToVisible( + new Rectangle( mChannelTable.getCellRect( translatedIndex, 0, true ) ) ); + } + } + + private Channel getSelectedChannel() + { + int index = mChannelTable.getSelectedRow(); + + if( index >= 0 ) + { + return mChannelModel.getChannelAtIndex( + mChannelTable.convertRowIndexToModel( index ) ); + } + + return null; + } + + /** + * Responds to New, Copy and Delete Channel button invocations + */ + @Override + public void actionPerformed( ActionEvent event ) + { + switch( event.getActionCommand() ) + { + case NEW_CHANNEL: + addChannel( new Channel( "New Channel" ) ); + break; + case COPY_CHANNEL: + Channel selected = getSelectedChannel(); + + if( selected != null ) + { + addChannel( selected.copyOf() ); + } + break; + case DELETE_CHANNEL: + Channel toDelete = getSelectedChannel(); + + if( toDelete != null ) + { + int choice = JOptionPane.showConfirmDialog( PlaylistEditor.this, + "Do you want to delete this channel?", "Delete Channel?", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); + + if( choice == JOptionPane.YES_OPTION ) + { + mChannelModel.removeChannel( toDelete ); + } + } + break; + default: + break; + } + } } diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index dea60a8c..c8b4498e 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -97,6 +97,9 @@ public class PlaylistManager implements ChannelEventListener */ private void transferPlaylistToModels() { +//TODO: delete the playlist after we've loaded it, since the models will have +//all of the data + mPlaylistLoading = true; mChannelModel.addChannels( mPlaylist.getChannels() ); @@ -167,6 +170,10 @@ public class PlaylistManager implements ChannelEventListener public void save() { +//TODO: recreate the playlist and load values from each of the models + + mPlaylist.setChannels( mChannelModel.getChannels() ); + JAXBContext context = null; SystemProperties props = SystemProperties.getInstance(); diff --git a/src/source/config/SourceConfigFactory.java b/src/source/config/SourceConfigFactory.java index 2cedeaec..f4d02f39 100644 --- a/src/source/config/SourceConfigFactory.java +++ b/src/source/config/SourceConfigFactory.java @@ -50,4 +50,39 @@ public class SourceConfigFactory return retVal; } + + /** + * Creates a copy of the configuration + */ + public static SourceConfiguration copy( SourceConfiguration config ) + { + if( config != null ) + { + switch( config.getSourceType() ) + { + case MIXER: + SourceConfigMixer originalMixer = (SourceConfigMixer)config; + SourceConfigMixer copyMixer = new SourceConfigMixer(); + copyMixer.setChannel( originalMixer.getChannel() ); + copyMixer.setMixer( originalMixer.getMixer() ); + return copyMixer; + case RECORDING: + SourceConfigRecording originalRec = (SourceConfigRecording)config; + SourceConfigRecording copyRec = new SourceConfigRecording(); + copyRec.setFrequency( originalRec.getFrequency() ); + copyRec.setRecordingAlias( originalRec.getRecordingAlias() ); + return copyRec; + case TUNER: + SourceConfigTuner originalTuner = (SourceConfigTuner)config; + SourceConfigTuner copyTuner = new SourceConfigTuner(); + copyTuner.setFrequency( originalTuner.getFrequency() ); + return copyTuner; + case NONE: + default: + return new SourceConfigNone(); + } + } + + return null; + } } From 15edcdebe66f09404c34653db8eef05df17a7249 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 24 Jan 2016 13:45:29 -0500 Subject: [PATCH 07/74] Adds AliasModel and AliasEvent and support for conversion from legacy playlist format. Renames PlaylistEditor to ChannelController --- src/alias/Alias.java | 36 ++- src/alias/AliasDirectory.java | 5 + src/alias/AliasEvent.java | 55 ++++ src/alias/AliasModel.java | 273 ++++++++++++++++++ src/alias/id/talkgroup/TalkgroupID.java | 5 - src/alias/id/talkgroup/TalkgroupIDEditor.java | 4 + src/controller/ControllerPanel.java | 6 +- .../channel/ChannelController.java} | 15 +- src/controller/channel/ChannelEvent.java | 2 +- src/controller/channel/ChannelModel.java | 6 +- src/gui/SDRTrunk.java | 6 +- src/instrument/gui/DecoderSelectionFrame.java | 4 +- src/playlist/Playlist.java | 21 ++ src/playlist/PlaylistManager.java | 181 +++++++----- 14 files changed, 526 insertions(+), 93 deletions(-) create mode 100644 src/alias/AliasEvent.java create mode 100644 src/alias/AliasModel.java rename src/{playlist/PlaylistEditor.java => controller/channel/ChannelController.java} (94%) diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 6bb82983..76fca7a1 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -29,6 +29,8 @@ import audio.inverted.AudioType; public class Alias { + private String mList; + private String mGroup; private String mName; private int mColor; private String mIconName; @@ -44,7 +46,7 @@ public class Alias return getName(); } - @XmlAttribute + @XmlAttribute( name="name" ) public String getName() { return mName; @@ -55,6 +57,38 @@ public class Alias mName = name; } + @XmlAttribute( name="list" ) + public String getList() + { + return mList; + } + + public void setList( String list ) + { + mList = list; + } + + public boolean hasList() + { + return mList != null; + } + + @XmlAttribute( name="group" ) + public String getGroup() + { + return mGroup; + } + + public void setGroup( String group ) + { + mGroup = group; + } + + public boolean hasGroup() + { + return mGroup != null; + } + @XmlAttribute public int getColor() { diff --git a/src/alias/AliasDirectory.java b/src/alias/AliasDirectory.java index 0e6003ec..34202b2f 100644 --- a/src/alias/AliasDirectory.java +++ b/src/alias/AliasDirectory.java @@ -40,6 +40,11 @@ public class AliasDirectory list.update(); } } + + public void clearAliasLists() + { + mAliasList.clear(); + } @XmlElement( name = "alias_list" ) public ArrayList getAliasList() diff --git a/src/alias/AliasEvent.java b/src/alias/AliasEvent.java new file mode 100644 index 00000000..c1cacd74 --- /dev/null +++ b/src/alias/AliasEvent.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias; + +public class AliasEvent +{ + private Alias mAlias; + private Event mEvent; + + /** + * AliasEvent - event describing any changes to an alias + * @param alias - alias that changed + * @param event - change event + */ + public AliasEvent( Alias alias, Event event ) + { + mAlias = alias; + mEvent = event; + } + + public Alias getAlias() + { + return mAlias; + } + + public Event getEvent() + { + return mEvent; + } + + /** + * Channel events to describe the specific event + */ + public enum Event + { + ADD, + CHANGE, + DELETE; + } +} diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java new file mode 100644 index 00000000..a9fe2540 --- /dev/null +++ b/src/alias/AliasModel.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.AliasEvent.Event; +import sample.Broadcaster; +import sample.Listener; + +/** + * Alias Model + */ +public class AliasModel implements TableModel +{ + private final static Logger mLog = LoggerFactory.getLogger( AliasModel.class ); + + private List mAliases = new ArrayList<>(); + private List mTableModelListeners = new CopyOnWriteArrayList<>(); + private Broadcaster mAliasEventBroadcaster = new Broadcaster<>(); + + public AliasModel() + { + } + + /** + * Unmodifiable list of all aliases currently in the model + */ + public List getAliases() + { + return Collections.unmodifiableList( mAliases ); + } + + public Alias getAliasAtIndex( int row ) + { + if( mAliases.size() >= row ) + { + return mAliases.get( row ); + } + + return null; + } + + /** + * Returns a list of unique alias list names from across the alias set + */ + public List getListNames() + { + List listNames = new ArrayList<>(); + + for( Alias alias: mAliases ) + { + if( alias.hasList() && !listNames.contains( alias.getList() ) ) + { + listNames.add( alias.getList() ); + } + } + + Collections.sort( listNames ); + + return listNames; + } + + /** + * Returns a list of alias group names for all aliases that have a matching + * list name value + */ + public List getGroupNames( String listName ) + { + List groupNames = new ArrayList<>(); + + if( listName != null ) + { + for( Alias alias: mAliases ) + { + if( alias.hasList() && + alias.hasGroup() && + listName.equals( alias.getList() ) && + !groupNames.contains( alias.getGroup() ) ) + { + groupNames.add( alias.getGroup() ); + } + } + } + + Collections.sort( groupNames ); + + return groupNames; + } + + /** + * Bulk loading of aliases + */ + public void addAliases( List aliases ) + { + for( Alias alias: aliases ) + { + addAlias( alias ); + } + } + + /** + * Adds the alias to the model + */ + public int addAlias( Alias alias ) + { + if( alias != null ) + { + mAliases.add( alias ); + + int index = mAliases.size() - 1; + + broadcast( new TableModelEvent( this, index, index, + TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT ) ); + + broadcast( new AliasEvent( alias, Event.ADD ) ); + + return index; + } + + return -1; + } + + /** + * Removes the channel from the model and broadcasts a channel remove event + */ + public void removeAlias( Alias alias ) + { + if( alias != null ) + { + int index = mAliases.indexOf( alias ); + + mAliases.remove( alias ); + + broadcast( new TableModelEvent( this, index, index, + TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); + + broadcast( new AliasEvent( alias, Event.DELETE ) ); + } + } + + @Override + public int getRowCount() + { + return mAliases.size(); + } + + @Override + public int getColumnCount() + { + return 5; + } + + @Override + public String getColumnName( int columnIndex ) + { + switch( columnIndex ) + { + case 0: + return "List"; + case 1: + return "Group"; + case 2: + return "Name"; + case 3: + return "Icon"; + case 4: + return "Color"; + } + + return null; + } + + @Override + public Class getColumnClass( int columnIndex ) + { + return String.class; + } + + @Override + public boolean isCellEditable( int rowIndex, int columnIndex ) + { + return false; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + Alias alias = mAliases.get( rowIndex ); + + switch( columnIndex ) + { + case 0: + return alias.getList(); + case 1: + return alias.getGroup(); + case 2: + return alias.getName(); + case 3: + return alias.getIconName(); + case 4: + return alias.getColor(); + } + + return null; + } + + @Override + public void setValueAt( Object aValue, int rowIndex, int columnIndex ) + { + throw new IllegalArgumentException( "Not yet implemented" ); + } + + private void broadcast( TableModelEvent event ) + { + for( TableModelListener listener: mTableModelListeners ) + { + listener.tableChanged( event ); + } + } + + @Override + public void addTableModelListener( TableModelListener listener ) + { + mTableModelListeners.add( listener ); + } + + @Override + public void removeTableModelListener( TableModelListener listener ) + { + mTableModelListeners.remove( listener ); + } + + public void broadcast( AliasEvent event ) + { + mAliasEventBroadcaster.broadcast( event ); + } + + public void addAliasEventListener( Listener listener ) + { + mAliasEventBroadcaster.addListener( listener ); + } + + public void removeAliasEventListener( Listener listener ) + { + mAliasEventBroadcaster.removeListener( listener ); + } +} diff --git a/src/alias/id/talkgroup/TalkgroupID.java b/src/alias/id/talkgroup/TalkgroupID.java index 5ca1df8a..2d8da48c 100644 --- a/src/alias/id/talkgroup/TalkgroupID.java +++ b/src/alias/id/talkgroup/TalkgroupID.java @@ -43,11 +43,6 @@ public class TalkgroupID extends AliasID mTalkgroup = talkgroup; } - public boolean hasAudioType() - { - return false; - } - public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index 1feda866..feec6d2d 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -36,6 +36,10 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener private TalkgroupIDNode mTalkgroupIDNode; private JTextField mTextTalkgroupID; +<<<<<<< Upstream, based on origin/master +======= + private JLabel mLabelAudio; +>>>>>>> 92e63f7 Adds AliasModel and AliasEvent and support for conversion from legacy playlist format. private String mHelpText = "Enter a formatted talkgroup identifier.\n\n" + "P25 Talkgroup Format: hexadecimal value, 4 characters for " diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index bbe7273b..8cb4232c 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -33,7 +33,6 @@ import module.decode.event.CallEventPanel; import module.decode.event.MessageActivityPanel; import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; -import playlist.PlaylistEditor; import playlist.PlaylistManager; import settings.SettingsManager; import source.SourceManager; @@ -44,6 +43,7 @@ import audio.AudioPanel; import com.jidesoft.swing.JideSplitPane; import com.jidesoft.swing.JideTabbedPane; +import controller.channel.ChannelController; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; @@ -59,7 +59,7 @@ public class ControllerPanel extends JPanel private ChannelSpectrumPanel mChannelSpectrumPanel; - private PlaylistEditor mPlaylistEditor; + private ChannelController mPlaylistEditor; private JideTabbedPane mTabbedPane; @@ -103,7 +103,7 @@ public class ControllerPanel extends JPanel mChannelStateList = new ChannelList( channelModel, channelProcessingManager, playlistManager, mSettingsManager ); - mPlaylistEditor = new PlaylistEditor( channelModel, playlistManager, sourceManager ); + mPlaylistEditor = new ChannelController( channelModel, playlistManager, sourceManager ); init(); } diff --git a/src/playlist/PlaylistEditor.java b/src/controller/channel/ChannelController.java similarity index 94% rename from src/playlist/PlaylistEditor.java rename to src/controller/channel/ChannelController.java index f48212cf..dbddfe84 100644 --- a/src/playlist/PlaylistEditor.java +++ b/src/controller/channel/ChannelController.java @@ -1,4 +1,4 @@ -package playlist; +package controller.channel; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -20,19 +20,16 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.PlaylistManager; import source.SourceManager; import com.jidesoft.swing.JideSplitPane; -import controller.channel.Channel; -import controller.channel.ChannelEditor; -import controller.channel.ChannelModel; - -public class PlaylistEditor extends JPanel +public class ChannelController extends JPanel implements ActionListener, ListSelectionListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( PlaylistEditor.class ); + private final static Logger mLog = LoggerFactory.getLogger( ChannelController.class ); private ChannelModel mChannelModel; private JTable mChannelTable; @@ -47,7 +44,7 @@ public class PlaylistEditor extends JPanel private JButton mCopyChannelButton = new JButton( COPY_CHANNEL ); private JButton mDeleteChannelButton = new JButton( DELETE_CHANNEL ); - public PlaylistEditor( ChannelModel channelModel, + public ChannelController( ChannelModel channelModel, PlaylistManager playlistManager, SourceManager sourceManager ) { @@ -205,7 +202,7 @@ public class PlaylistEditor extends JPanel if( toDelete != null ) { - int choice = JOptionPane.showConfirmDialog( PlaylistEditor.this, + int choice = JOptionPane.showConfirmDialog( ChannelController.this, "Do you want to delete this channel?", "Delete Channel?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java index a4166a2d..b71b5c94 100644 --- a/src/controller/channel/ChannelEvent.java +++ b/src/controller/channel/ChannelEvent.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java index ab1e3a88..0ec88eb9 100644 --- a/src/controller/channel/ChannelModel.java +++ b/src/controller/channel/ChannelModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -26,11 +26,11 @@ import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.TableModel; +import module.decode.DecoderType; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistEditor; -import module.decode.DecoderType; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent.Event; diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 4447562f..41f5987e 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -60,6 +60,7 @@ import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import spectrum.SpectralDisplayPanel; import util.TimeStamp; +import alias.AliasModel; import alias.action.AliasActionManager; import audio.AudioManager; @@ -118,9 +119,12 @@ public class SDRTrunk SourceManager sourceManager = new SourceManager( mSettingsManager, threadPoolManager ); + AliasModel aliasModel = new AliasModel(); + ChannelModel channelModel = new ChannelModel(); - PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, channelModel ); + PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, + aliasModel, channelModel ); RecorderManager recorderManager = new RecorderManager( threadPoolManager ); diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index 8c242b66..d96020b1 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -13,6 +13,7 @@ import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; import source.IControllableFileSource; +import alias.AliasModel; import controller.ThreadPoolManager; import controller.channel.ChannelModel; @@ -29,11 +30,12 @@ public class DecoderSelectionFrame extends JInternalFrame public DecoderSelectionFrame( JDesktopPane desktop, IControllableFileSource source ) { + AliasModel aliasModel = new AliasModel(); ChannelModel channelModel = new ChannelModel(); ThreadPoolManager tpm = new ThreadPoolManager(); - mPlaylistManager = new PlaylistManager( tpm, channelModel ); + mPlaylistManager = new PlaylistManager( tpm, aliasModel, channelModel ); mDesktop = desktop; mSource = source; diff --git a/src/playlist/Playlist.java b/src/playlist/Playlist.java index 0d63153f..d4ae9d0c 100644 --- a/src/playlist/Playlist.java +++ b/src/playlist/Playlist.java @@ -24,12 +24,14 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import alias.Alias; import alias.AliasDirectory; import controller.channel.Channel; import controller.channel.map.ChannelMapList; import controller.system.SystemList; @XmlSeeAlso( { AliasDirectory.class, + Alias.class, Channel.class, ChannelMapList.class, SystemList.class } ) @@ -40,12 +42,24 @@ public class Playlist private AliasDirectory mAliasDirectory = new AliasDirectory(); private ChannelMapList mChannelMapList = new ChannelMapList(); private SystemList mSystemList = new SystemList(); + private List mAliases = new ArrayList<>(); private List mChannels = new ArrayList<>(); public Playlist() { } + @XmlElement( name = "alias" ) + public List getAliases() + { + return mAliases; + } + + public void setAliases( List aliases ) + { + mAliases = aliases; + } + @XmlElement( name = "channel" ) public List getChannels() { @@ -70,6 +84,13 @@ public class Playlist mAliasDirectory = directory; } + public boolean hasAliasDirectory() + { + return mAliasDirectory != null && + mAliasDirectory.getAliasList() != null && + !mAliasDirectory.getAliasList().isEmpty(); + } + @XmlElement( name = "system_list" ) public SystemList getSystemList() { diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index c8b4498e..ae97cfbb 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -37,10 +37,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import properties.SystemProperties; -import controller.channel.Channel; -import controller.channel.ChannelModel; -import controller.site.Site; -import controller.system.SystemList; +import alias.Alias; +import alias.AliasDirectory; +import alias.AliasList; +import alias.AliasModel; +import alias.Group; import controller.ThreadPoolManager; import controller.channel.Channel; import controller.channel.Channel.ChannelType; @@ -57,10 +58,8 @@ public class PlaylistManager implements ChannelEventListener private Playlist mPlaylist = new Playlist(); - private ChannelModel mChannelModel; - - public PlaylistManager( ChannelModel channelModel ) private ThreadPoolManager mThreadPoolManager; + private AliasModel mAliasModel; private ChannelModel mChannelModel; private AtomicBoolean mPlaylistSavePending = new AtomicBoolean(); @@ -76,10 +75,12 @@ public class PlaylistManager implements ChannelEventListener * @param threadPoolManager * @param channelModel */ - public PlaylistManager( ThreadPoolManager threadPoolManager, + public PlaylistManager( ThreadPoolManager threadPoolManager, + AliasModel aliasModel, ChannelModel channelModel ) { mThreadPoolManager = threadPoolManager; + mAliasModel = aliasModel; mChannelModel = channelModel; //Register for channel events so that we can save the playlist when @@ -101,7 +102,8 @@ public class PlaylistManager implements ChannelEventListener //all of the data mPlaylistLoading = true; - + + mAliasModel.addAliases( mPlaylist.getAliases() ); mChannelModel.addChannels( mPlaylist.getChannels() ); mPlaylistLoading = false; @@ -168,11 +170,57 @@ public class PlaylistManager implements ChannelEventListener } } + /** + * Channel event listener method. Monitors channel events for events that + * indicate that the playlist has changed and queues automatic playlist + * saving. + */ + @Override + public void channelChanged( ChannelEvent event ) + { + //Only save playlist for changes to standard channels (not traffic) + if( event.getChannel().getChannelType() == ChannelType.STANDARD ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_ADD: + case NOTIFICATION_CONFIGURATION_CHANGE: + case NOTIFICATION_DELETE: + case NOTIFICATION_PROCESSING_START: + case NOTIFICATION_PROCESSING_STOP: + schedulePlaylistSave(); + break; + case NOTIFICATION_ENABLE_REJECTED: + case NOTIFICATION_SELECTION_CHANGE: + case NOTIFICATION_STATE_RESET: + case REQUEST_DELETE: + case REQUEST_DESELECT: + case REQUEST_DISABLE: + case REQUEST_ENABLE: + case REQUEST_SELECT: + //Do nothing for these event types + break; + default: + //When a new event enum entry is added and received, throw an + //exception here to ensure developer adds support for the + //use case + throw new IllegalArgumentException( "Unrecognized Channel " + + "Event [" + event.getEvent().name() + "]" ); + } + } + } + public void save() { //TODO: recreate the playlist and load values from each of the models + mLog.debug( "Aliases in model:" + mAliasModel.getAliases().size() ); + mPlaylist.setAliases( mAliasModel.getAliases() ); + mLog.debug( "Aliases in playlist:" + mPlaylist.getAliases().size() ); + + mLog.debug( "Channels in model:" + mChannelModel.getChannels().size() ); mPlaylist.setChannels( mChannelModel.getChannels() ); + mLog.debug( "Channels in playlist:" + mPlaylist.getChannels().size() ); JAXBContext context = null; @@ -370,16 +418,27 @@ public class PlaylistManager implements ChannelEventListener playlistPath.toString() + "]" ); } + boolean saveRequired = false; + if( mPlaylist == null ) { mPlaylist = new Playlist(); - save(); + saveRequired = true; } + //Check for and convert from legacy play list format - if( mPlaylist.hasSystemList() ) + if( mPlaylist.hasSystemList() || mPlaylist.hasAliasDirectory() ) { convertPlaylistFormat(); + saveRequired = true; + } + + transferPlaylistToModels(); + + if( saveRequired ) + { + save(); } } @@ -388,35 +447,60 @@ public class PlaylistManager implements ChannelEventListener */ private void convertPlaylistFormat() { - mLog.info( "Legacy playlist format detected - converting ..." ); + mLog.info( "Legacy playlist format detected - converting" ); createBackupPlaylist(); //Playlist version 1 to version 2 format conversion. In order to keep //backwards compatibility, transfer all of the System-Site-Channel //objects over to the new channel list format - SystemList systemList = mPlaylist.getSystemList(); - - for( controller.system.System system: systemList.getSystem() ) + if( mPlaylist.hasSystemList() ) { - for( Site site: system.getSite() ) + SystemList systemList = mPlaylist.getSystemList(); + + for( controller.system.System system: systemList.getSystem() ) { - for( Channel channel: site.getChannel() ) + for( Site site: system.getSite() ) { - channel.setSystem( system.getName() ); - channel.setSite( site.getName() ); - - mPlaylist.getChannels().add( channel ); + for( Channel channel: site.getChannel() ) + { + channel.setSystem( system.getName() ); + channel.setSite( site.getName() ); + + mPlaylist.getChannels().add( channel ); + } } } + + mPlaylist.getSystemList().clearSystems(); + + mLog.info( "Converted [" + mPlaylist.getChannels().size() + + "] channels to new playlist format" ); + } + + if( mPlaylist.hasAliasDirectory() ) + { + AliasDirectory aliasDirectory = mPlaylist.getAliasDirectory(); + + for( AliasList list: aliasDirectory.getAliasList() ) + { + for( Group group: list.getGroup() ) + { + for( Alias alias: group.getAlias() ) + { + alias.setList( list.getName() ); + alias.setGroup( group.getName() ); + + mPlaylist.getAliases().add( alias ); + } + } + } + + mPlaylist.getAliasDirectory().clearAliasLists(); + + mLog.info( "Converted [" + mPlaylist.getAliases().size() + + "] aliases to new playlist format" ); } - - mPlaylist.getSystemList().clearSystems(); - - mLog.info( "Converted [" + mPlaylist.getChannels().size() + - "] channels to new playlist format" ); - - save(); } /** @@ -448,46 +532,5 @@ public class PlaylistManager implements ChannelEventListener mPlaylistSavePending.set( false ); } - - //Check for and convert from legacy play list format - if( mPlaylist.hasSystemList() ) - { - convertPlaylistFormat(); - } - } - - /** - * Converts playlist data over to new format - */ - private void convertPlaylistFormat() - { - mLog.info( "Legacy playlist format detected - converting ..." ); - - createBackupPlaylist(); - - //Playlist version 1 to version 2 format conversion. In order to keep - //backwards compatibility, transfer all of the System-Site-Channel - //objects over to the new channel list format - SystemList systemList = mPlaylist.getSystemList(); - - for( controller.system.System system: systemList.getSystem() ) - { - for( Site site: system.getSite() ) - { - for( Channel channel: site.getChannel() ) - { - channel.setSystem( system.getName() ); - channel.setSite( site.getName() ); - - mPlaylist.getChannels().add( channel ); - } - } - } - - mPlaylist.getSystemList().clearSystems(); - - mLog.info( "Converted [" + mPlaylist.getChannels().size() + "] channels to new playlist format" ); - - save(); } } From 75714072b239c8eb2fdbc36446253777a33425c4 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 24 Jan 2016 15:46:55 -0500 Subject: [PATCH 08/74] Adds AliasModel, AliasController tab with new AliasEditor --- src/alias/Alias.java | 5 + src/alias/AliasActionListener.java | 30 ++ src/alias/AliasConfigurationEditor.java | 36 +++ src/alias/AliasController.java | 287 ++++++++++++++++++ src/alias/AliasEditor.java | 205 ++++--------- src/alias/AliasModel.java | 31 +- src/alias/AliasNameEditor.java | 232 ++++++++++++++ src/alias/AliasNode.java | 3 +- src/controller/ControllerPanel.java | 14 +- src/controller/channel/ChannelController.java | 2 +- src/controller/channel/ChannelEditor.java | 2 +- src/gui/SDRTrunk.java | 2 +- 12 files changed, 686 insertions(+), 163 deletions(-) create mode 100644 src/alias/AliasActionListener.java create mode 100644 src/alias/AliasConfigurationEditor.java create mode 100644 src/alias/AliasController.java create mode 100644 src/alias/AliasNameEditor.java diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 76fca7a1..0634c434 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -41,6 +41,11 @@ public class Alias { } + public Alias( String name ) + { + mName = name; + } + public String toString() { return getName(); diff --git a/src/alias/AliasActionListener.java b/src/alias/AliasActionListener.java new file mode 100644 index 00000000..5c97948c --- /dev/null +++ b/src/alias/AliasActionListener.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias; + +/** + * Interface for receiving alias events. Each listener will receive + * notification of all alias events. + */ +public interface AliasActionListener +{ + /** + * Notifies the listener that an alias event has occurred + */ + public void aliasChanged( AliasEvent event ); +} diff --git a/src/alias/AliasConfigurationEditor.java b/src/alias/AliasConfigurationEditor.java new file mode 100644 index 00000000..1add4761 --- /dev/null +++ b/src/alias/AliasConfigurationEditor.java @@ -0,0 +1,36 @@ +/******************************************************************************* + + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias; + +import javax.swing.JPanel; + +public abstract class AliasConfigurationEditor extends JPanel +{ + private static final long serialVersionUID = 1L; + + /** + * Sets or resets the configuration editor components + */ + public abstract void setAlias( Alias alias ); + + /** + * Saves the contents of the editor components to the configuration + */ + public abstract void save(); +} diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java new file mode 100644 index 00000000..65844e57 --- /dev/null +++ b/src/alias/AliasController.java @@ -0,0 +1,287 @@ +package alias; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Rectangle; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; + +import net.coderazzi.filters.gui.AutoChoices; +import net.coderazzi.filters.gui.TableFilterHeader; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import settings.SettingsManager; + +import com.jidesoft.swing.JideSplitPane; + +public class AliasController extends JPanel + implements ActionListener, ListSelectionListener +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( AliasController.class ); + + private AliasModel mAliasModel; + private JTable mAliasTable; + private TableFilterHeader mTableFilterHeader; + private AliasEditor mEditor; + + private static final String NEW_ALIAS = "New"; + private static final String COPY_ALIAS = "Copy"; + private static final String DELETE_ALIAS = "Delete"; + + private JButton mNewButton = new JButton( NEW_ALIAS ); + private JButton mCopyButton = new JButton( COPY_ALIAS ); + private JButton mDeleteButton = new JButton( DELETE_ALIAS ); + + private IconCellRenderer mIconCellRenderer; + + public AliasController( AliasModel aliasModel, SettingsManager settingsManager ) + { + mAliasModel = aliasModel; + + mEditor = new AliasEditor( mAliasModel, settingsManager ); + mAliasModel.addAliasEventListener( mEditor ); + + mIconCellRenderer = new IconCellRenderer( settingsManager ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", + "[grow,fill]", + "[grow,fill]") ); + + //System Configuration View and Editor + mAliasTable = new JTable( mAliasModel ); + mAliasTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mAliasTable.getSelectionModel().addListSelectionListener( this ); + mAliasTable.setAutoCreateRowSorter( true ); + + mAliasTable.getColumnModel().getColumn( AliasModel.COLUMN_COLOR ) + .setCellRenderer( new ColorCellRenderer() ); + + mAliasTable.getColumnModel().getColumn( AliasModel.COLUMN_ICON ) + .setCellRenderer( mIconCellRenderer ); + + mTableFilterHeader = new TableFilterHeader( mAliasTable, AutoChoices.ENABLED ); + mTableFilterHeader.setFilterOnUpdates( true ); + + JScrollPane tableScroller = new JScrollPane( mAliasTable ); + + JPanel buttonsPanel = new JPanel(); + + buttonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); + + mNewButton.addActionListener( this ); + mNewButton.setToolTipText( "Adds a new alias" ); + buttonsPanel.add( mNewButton ); + + mCopyButton.addActionListener( this ); + mCopyButton.setEnabled( false ); + mCopyButton.setToolTipText( "Creates a copy of the currently selected alias and adds it" ); + buttonsPanel.add( mCopyButton ); + + mDeleteButton.addActionListener( this ); + mDeleteButton.setEnabled( false ); + mDeleteButton.setToolTipText( "Deletes the currently selected alias" ); + buttonsPanel.add( mDeleteButton ); + + JPanel listAndButtonsPanel = new JPanel(); + + listAndButtonsPanel.setLayout( + new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); + + listAndButtonsPanel.add( tableScroller, "wrap" ); + listAndButtonsPanel.add( buttonsPanel ); + + JideSplitPane splitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + splitPane.setDividerSize( 5 ); + splitPane.setShowGripper( true ); + splitPane.add( listAndButtonsPanel ); + splitPane.add( mEditor ); + + add( splitPane ); + } + + @Override + public void valueChanged( ListSelectionEvent event ) + { + //This limits event firing to only when selection is complete + if( !event.getValueIsAdjusting() ) + { + int selectedRow = mAliasTable.getSelectedRow(); + + if( selectedRow != -1 ) + { + int index = mAliasTable.convertRowIndexToModel( mAliasTable.getSelectedRow() ); + + Alias alias = mAliasModel.getAliasAtIndex( index ); + + if( alias != null ) + { + mEditor.setAlias( alias ); + mCopyButton.setEnabled( true ); + mDeleteButton.setEnabled( true ); + } + } + else + { + mEditor.setAlias( null ); + mCopyButton.setEnabled( false ); + mDeleteButton.setEnabled( false ); + } + } + } + + /** + * Adds the alias to the alias table/model and scrolls the view it + */ + private void addAlias( Alias alias ) + { + //HACK: when inserting a row to the model, the JTable gets + //notified and attempts to tell the coderazzi table filter + //adaptive choices filter to refresh before the table filter is + //notified of the row additions, causing an index out of bounds + //exception. We turn off adaptive choices temporarily, add the + //channel, and turn on adaptive choices again. + mTableFilterHeader.setAdaptiveChoices( false ); + + int index = mAliasModel.addAlias( alias ); + + mTableFilterHeader.setAdaptiveChoices( true ); + + if( index >= 0 ) + { + int translatedIndex = mAliasTable.convertRowIndexToView( index ); + mAliasTable.setRowSelectionInterval( translatedIndex, translatedIndex ); + mAliasTable.scrollRectToVisible( + new Rectangle( mAliasTable.getCellRect( translatedIndex, 0, true ) ) ); + } + } + + private Alias getSelectedAlias() + { + int index = mAliasTable.getSelectedRow(); + + if( index >= 0 ) + { + return mAliasModel.getAliasAtIndex( + mAliasTable.convertRowIndexToModel( index ) ); + } + + return null; + } + + /** + * Responds to New, Copy and Delete Channel button invocations + */ + @Override + public void actionPerformed( ActionEvent event ) + { + switch( event.getActionCommand() ) + { + case NEW_ALIAS: + addAlias( new Alias( "New Alias" ) ); + break; + case COPY_ALIAS: + Alias selected = getSelectedAlias(); + + if( selected != null ) + { +// addAlias( selected.copyOf() ); + } + break; + case DELETE_ALIAS: + Alias toDelete = getSelectedAlias(); + + if( toDelete != null ) + { + int choice = JOptionPane.showConfirmDialog( AliasController.this, + "Do you want to delete this alias?", "Delete Alias?", + JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); + + if( choice == JOptionPane.YES_OPTION ) + { + mAliasModel.removeAlias( toDelete ); + } + } + break; + default: + break; + } + } + + /** + * Colorizes the cell based on the cell's integer value + */ + public class ColorCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + JLabel label = (JLabel)super.getTableCellRendererComponent( table, + value, isSelected, hasFocus, row, column ); + + label.setBackground( new Color( (int)value ) ); + + label.setText( "" ); + + return label; + } + } + + /** + * Displays cell's icon and name + */ + public class IconCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + private SettingsManager mSettingsManager; + + public IconCellRenderer( SettingsManager settingsManager ) + { + mSettingsManager = settingsManager; + } + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + JLabel label = (JLabel)super.getTableCellRendererComponent( table, + value, isSelected, hasFocus, row, column ); + + ImageIcon icon = mSettingsManager.getImageIcon( label.getText(), 12 ); + + if( icon != null ) + { + label.setIcon( icon ); + } + + return label; + } + } +} diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index cb4f9682..38e4c78d 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -18,133 +18,76 @@ package alias; import java.awt.Color; -import java.awt.Dimension; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.BorderFactory; import javax.swing.JButton; -import javax.swing.JColorChooser; -import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; +import javax.swing.JSeparator; -import map.IconManager; -import map.MapIcon; -import map.MapIconListCellRenderer; import net.miginfocom.swing.MigLayout; +import sample.Listener; import settings.SettingsManager; -import controller.ConfigurableNode; +import alias.AliasEvent.Event; -public class AliasEditor extends JPanel implements ActionListener +import com.jidesoft.swing.JideTabbedPane; + +public class AliasEditor extends JPanel implements ActionListener, Listener { private static final long serialVersionUID = 1L; - private AliasNode mAliasNode; - private SettingsManager mSettingsManager; - - private JLabel mLabelName; - private JTextField mTextAlias; + private static final String DEFAULT_NAME = "select an alias"; - private JLabel mLabelMapColor; - private JButton mButtonColor; - private JComboBox mComboMapIcon; - private JButton mBtnIconManager; + private AliasModel mAliasModel; + + private JLabel mAliasLabel; + private AliasNameEditor mAliasNameEditor; - public AliasEditor( AliasNode aliasNode, - SettingsManager settingsManager ) + private Alias mAlias; + + + public AliasEditor( AliasModel aliasModel, SettingsManager settingsManager ) { - mAliasNode = aliasNode; - mSettingsManager = settingsManager; + mAliasModel = aliasModel; + mAliasNameEditor = new AliasNameEditor( mAliasModel, settingsManager ); initGUI(); } + + public void setAlias( Alias alias ) + { + mAlias = alias; + + if( mAlias != null ) + { + mAliasLabel.setText( alias.getName() ); + } + else + { + mAliasLabel.setText( DEFAULT_NAME ); + } + + mAliasNameEditor.setAlias( alias ); + + } private void initGUI() { - setLayout( new MigLayout( "", "[right][grow,fill]", "[][][][][][grow][]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", "[][][][][grow,fill]" ) ); - setBorder( BorderFactory.createTitledBorder( "Alias" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName ); + add( new JLabel( "Alias:" ) ); + mAliasLabel = new JLabel( DEFAULT_NAME ); + add( mAliasLabel ); - mTextAlias = new JTextField( mAliasNode.getAlias().getName() ); - add( mTextAlias, "wrap" ); - - mLabelMapColor = new JLabel( "Map Color:" ); - add( mLabelMapColor ); - - mButtonColor = new JButton( "Map Color" ); - mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() ); - mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() ); - mButtonColor.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Color newColor = JColorChooser.showDialog( - AliasEditor.this, - "Choose color for map labels and routes", - mAliasNode.getAlias().getMapColor() ); - - mButtonColor.setForeground( newColor ); - mButtonColor.setBackground( newColor ); - } - } ); - add( mButtonColor, "wrap" ); + add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" ); - add( new JLabel( "Icon:" ) ); + JideTabbedPane tabs = new JideTabbedPane(); + tabs.setFont( this.getFont() ); + tabs.setForeground( Color.BLACK ); - mComboMapIcon = new JComboBox( mSettingsManager.getMapIcons() ); - - MapIconListCellRenderer renderer = new MapIconListCellRenderer(); - renderer.setPreferredSize( new Dimension( 200, 30 ) ); - mComboMapIcon.setRenderer( renderer ); - - String iconName = mAliasNode.getAlias().getIconName(); - - if( iconName == null ) - { - iconName = SettingsManager.DEFAULT_ICON; - } - - MapIcon savedIcon = mSettingsManager.getMapIcon( iconName ); - - if( savedIcon != null ) - { - mComboMapIcon.setSelectedItem( savedIcon ); - } - - add( mComboMapIcon, "wrap" ); - - //Dummy place holder - add( new JLabel() ); - - mBtnIconManager = new JButton( "Icon Manager" ); - mBtnIconManager.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - final IconManager iconManager = - new IconManager( mSettingsManager, AliasEditor.this ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - iconManager.setVisible( true ); - } - } ); - } - } ); - add( mBtnIconManager, "span 2,wrap" ); + tabs.addTab( "Alias", mAliasNameEditor ); + add( tabs, "span,grow" ); JButton btnSave = new JButton( "Save" ); btnSave.addActionListener( AliasEditor.this ); @@ -155,6 +98,16 @@ public class AliasEditor extends JPanel implements ActionListener add( btnReset, "wrap" ); } + @Override + public void receive( AliasEvent event ) + { + //If this is the currently displayed alias, reload it + if( mAlias != null && mAlias == event.getAlias() ) + { + setAlias( event.getAlias() ); + } + } + @Override public void actionPerformed( ActionEvent e ) { @@ -162,54 +115,14 @@ public class AliasEditor extends JPanel implements ActionListener if( command.contentEquals( "Save" ) ) { - String alias = mTextAlias.getText(); + mAliasNameEditor.save(); - if( alias != null ) - { - boolean expanded = mAliasNode.getModel().getTree() - .isExpanded( new TreePath( mAliasNode ) ); - - mAliasNode.getAlias().setName( alias ); - ((ConfigurableNode)mAliasNode.getParent()).sort(); - - mAliasNode.getAlias().setColor( - mButtonColor.getBackground().getRGB() ); - - MapIcon selectedIcon = (MapIcon)mComboMapIcon.getSelectedItem(); - - if( selectedIcon != null ) - { - mAliasNode.getAlias().setIconName( selectedIcon.getName() ); - } - - mAliasNode.save(); - - mAliasNode.show(); - - if( expanded ) - { - mAliasNode.getModel().getTree() - .expandPath( new TreePath( mAliasNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( AliasEditor.this, "Please enter an alias name" ); - } + //Broadcast an alias change event to save the updates + mAliasModel.broadcast( new AliasEvent( mAlias, Event.CHANGE ) ); } else if( command.contentEquals( "Reset" ) ) { - mTextAlias.setText( mAliasNode.getAlias().getName() ); - - mButtonColor.setBackground( mAliasNode.getAlias().getMapColor() ); - mButtonColor.setForeground( mAliasNode.getAlias().getMapColor() ); - - MapIcon savedIcon = mSettingsManager - .getMapIcon( mAliasNode.getAlias().getIconName() ); - - mComboMapIcon.setSelectedItem( savedIcon ); + setAlias( mAlias ); } - - mAliasNode.refresh(); } } diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index a9fe2540..4a1d11b1 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -39,6 +39,12 @@ import sample.Listener; public class AliasModel implements TableModel { private final static Logger mLog = LoggerFactory.getLogger( AliasModel.class ); + + public static final int COLUMN_LIST = 0; + public static final int COLUMN_GROUP = 1; + public static final int COLUMN_NAME = 2; + public static final int COLUMN_ICON = 3; + public static final int COLUMN_COLOR = 4; private List mAliases = new ArrayList<>(); private List mTableModelListeners = new CopyOnWriteArrayList<>(); @@ -181,15 +187,15 @@ public class AliasModel implements TableModel { switch( columnIndex ) { - case 0: + case COLUMN_LIST: return "List"; - case 1: + case COLUMN_GROUP: return "Group"; - case 2: + case COLUMN_NAME: return "Name"; - case 3: + case COLUMN_ICON: return "Icon"; - case 4: + case COLUMN_COLOR: return "Color"; } @@ -199,6 +205,11 @@ public class AliasModel implements TableModel @Override public Class getColumnClass( int columnIndex ) { + if( columnIndex == COLUMN_COLOR ) + { + return Integer.class; + } + return String.class; } @@ -215,15 +226,15 @@ public class AliasModel implements TableModel switch( columnIndex ) { - case 0: + case COLUMN_LIST: return alias.getList(); - case 1: + case COLUMN_GROUP: return alias.getGroup(); - case 2: + case COLUMN_NAME: return alias.getName(); - case 3: + case COLUMN_ICON: return alias.getIconName(); - case 4: + case COLUMN_COLOR: return alias.getColor(); } diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java new file mode 100644 index 00000000..742fde37 --- /dev/null +++ b/src/alias/AliasNameEditor.java @@ -0,0 +1,232 @@ +package alias; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JTextField; + +import map.IconManager; +import map.MapIcon; +import map.MapIconListCellRenderer; +import net.miginfocom.swing.MigLayout; +import settings.SettingsManager; + +public class AliasNameEditor extends AliasConfigurationEditor +{ + private static final long serialVersionUID = 1L; + + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); + + private JComboBox mListCombo = new JComboBox<>( EMPTY_MODEL ); + private JComboBox mGroupCombo = new JComboBox<>( EMPTY_MODEL ); + private JTextField mName; + + private JLabel mLabelMapColor; + private JButton mButtonColor; + private JComboBox mComboMapIcon; + private JButton mBtnIconManager; + + private Alias mAlias; + private AliasModel mAliasModel; + private SettingsManager mSettingsManager; + + public AliasNameEditor( AliasModel aliasModel, SettingsManager settingsManager ) + { + mAliasModel = aliasModel; + mSettingsManager = settingsManager; + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", "[][][][][][grow][]" ) ); + + add( new JLabel( "List:" ) ); + mListCombo.setEditable( true ); + add( mListCombo, "wrap" ); + + add( new JLabel( "Group:" ) ); + mGroupCombo.setEditable( true ); + add( mGroupCombo, "wrap" ); + + add( new JLabel( "Name:" ) ); + mName = new JTextField(); + add( mName, "wrap" ); + + add( new JLabel( "Color:" ) ); + + mButtonColor = new JButton( "Select ..." ); + mButtonColor.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Color newColor = JColorChooser.showDialog( + AliasNameEditor.this, + "Choose color for this alias", + ( mAlias != null ? mAlias.getMapColor() : null ) ); + + if( newColor != null ) + { + mButtonColor.setForeground( newColor ); + mButtonColor.setBackground( newColor ); + } + } + } ); + add( mButtonColor, "wrap" ); + + add( new JLabel( "Icon:" ) ); + + mComboMapIcon = new JComboBox( mSettingsManager.getMapIcons() ); + + MapIconListCellRenderer renderer = new MapIconListCellRenderer(); + renderer.setPreferredSize( new Dimension( 200, 30 ) ); + mComboMapIcon.setRenderer( renderer ); + + add( mComboMapIcon, "wrap" ); + + //Dummy place holder + add( new JLabel() ); + + mBtnIconManager = new JButton( "Icon Manager" ); + mBtnIconManager.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + final IconManager iconManager = + new IconManager( mSettingsManager, AliasNameEditor.this ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); + } + } ); + add( mBtnIconManager, "span 2,wrap" ); + + } + + @Override + public void setAlias( Alias alias ) + { + mAlias = alias; + + if( mAlias != null ) + { + mName.setText( alias.getName() ); + + List listNames = mAliasModel.getListNames(); + + if( listNames.isEmpty() ) + { + mListCombo.setModel( EMPTY_MODEL ); + } + else + { + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) );; + } + + mListCombo.setSelectedItem( mAlias.getList() ); + + List groupNames = mAliasModel.getGroupNames( mAlias.getList() ); + + if( groupNames.isEmpty() ) + { + mGroupCombo.setModel( EMPTY_MODEL ); + } + else + { + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) );; + } + + mGroupCombo.setSelectedItem( mAlias.getGroup() ); + + Color color = mAlias.getMapColor(); + + mButtonColor.setBackground( color ); + mButtonColor.setForeground( color ); + + String iconName = mAlias.getIconName(); + + if( iconName == null ) + { + iconName = SettingsManager.DEFAULT_ICON; + } + + MapIcon savedIcon = mSettingsManager.getMapIcon( iconName ); + + if( savedIcon != null ) + { + mComboMapIcon.setSelectedItem( savedIcon ); + } + } + else + { + mListCombo.setModel( EMPTY_MODEL ); + mGroupCombo.setModel( EMPTY_MODEL ); + mName.setText( null ); + + mButtonColor.setBackground( getBackground() ); + mButtonColor.setForeground( getForeground() ); + } + } + + @Override + public void save() + { + // TODO Auto-generated method stub + String alias = mName.getText(); + + if( alias != null ) + { +// boolean expanded = mAliasNode.getModel().getTree() +// .isExpanded( new TreePath( mAliasNode ) ); +// +// mAliasNode.getAlias().setName( alias ); +// ((ConfigurableNode)mAliasNode.getParent()).sort(); +// +// mAliasNode.getAlias().setColor( +// mButtonColor.getBackground().getRGB() ); + + MapIcon selectedIcon = (MapIcon)mComboMapIcon.getSelectedItem(); + +// if( selectedIcon != null ) +// { +// mAliasNode.getAlias().setIconName( selectedIcon.getName() ); +// } +// +// mAliasNode.save(); +// +// mAliasNode.show(); +// +// if( expanded ) +// { +// mAliasNode.getModel().getTree() +// .expandPath( new TreePath( mAliasNode ) ); +// } + } + else + { + JOptionPane.showMessageDialog( AliasNameEditor.this, "Please enter an alias name" ); + } + + } +} diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java index 070e3b60..7346bb4c 100644 --- a/src/alias/AliasNode.java +++ b/src/alias/AliasNode.java @@ -81,7 +81,8 @@ public class AliasNode extends ConfigurableNode @Override public JPanel getEditor() { - return new AliasEditor( this, mSettingsManager ); + return null; +// return new AliasEditor( this, mSettingsManager ); } public Alias getAlias() diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index 8cb4232c..ef7d8619 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -37,6 +37,8 @@ import playlist.PlaylistManager; import settings.SettingsManager; import source.SourceManager; import spectrum.ChannelSpectrumPanel; +import alias.AliasController; +import alias.AliasModel; import audio.AudioManager; import audio.AudioPanel; @@ -58,8 +60,10 @@ public class ControllerPanel extends JPanel private MessageActivityPanel mMessageActivityPanel; private ChannelSpectrumPanel mChannelSpectrumPanel; + + private AliasController mAliasController; - private ChannelController mPlaylistEditor; + private ChannelController mChannelController; private JideTabbedPane mTabbedPane; @@ -77,6 +81,7 @@ public class ControllerPanel extends JPanel public ControllerPanel( AudioManager audioManager, ConfigurationControllerModel controller, + AliasModel aliasModel, ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, MapService mapService, @@ -103,7 +108,9 @@ public class ControllerPanel extends JPanel mChannelStateList = new ChannelList( channelModel, channelProcessingManager, playlistManager, mSettingsManager ); - mPlaylistEditor = new ChannelController( channelModel, playlistManager, sourceManager ); + mChannelController = new ChannelController( channelModel, playlistManager, sourceManager ); + + mAliasController = new AliasController( aliasModel, mSettingsManager ); init(); } @@ -129,7 +136,8 @@ public class ControllerPanel extends JPanel mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont( this.getFont() ); mTabbedPane.setForeground( Color.BLACK ); - mTabbedPane.addTab( "Channels", mPlaylistEditor ); + mTabbedPane.addTab( "Channels", mChannelController ); + mTabbedPane.addTab( "Aliases", mAliasController ); mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java index dbddfe84..88b9fe9e 100644 --- a/src/controller/channel/ChannelController.java +++ b/src/controller/channel/ChannelController.java @@ -140,7 +140,7 @@ public class ChannelController extends JPanel } /** - * Selects the channel's table row and scrolls the table's viewport to show it + * Adds the channel to the channel model/table and scrolls to view it */ private void addChannel( Channel channel ) { diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 4e6c85e9..2e77fcab 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -120,7 +120,7 @@ public class ChannelEditor extends JPanel private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow]" ) ); mEnableButton.addActionListener( this ); mEnableButton.setEnabled( false ); diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 41f5987e..473ea3e7 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -161,7 +161,7 @@ public class SDRTrunk playlistManager, mSettingsManager, sourceManager ); mControllerPanel = new ControllerPanel( audioManager, configurationControllerModel, - channelModel, channelProcessingManager, mapService, playlistManager, + aliasModel, channelModel, channelProcessingManager, mapService, playlistManager, mSettingsManager, sourceManager ); mSpectralPanel = new SpectralDisplayPanel( configurationControllerModel, From bca39e74cee4b56f3f531cc8251700c76c7fec04 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 24 Jan 2016 19:27:26 -0500 Subject: [PATCH 09/74] Work in progress - more updates to the alias controller and alias editor panels. --- src/alias/Alias.java | 26 ----- src/alias/AliasController.java | 2 +- src/alias/AliasEditor.java | 14 ++- src/alias/AliasFactory.java | 138 +++++++++++++++++++++++++++ src/alias/AliasID.java | 12 --- src/alias/AliasIdentifierEditor.java | 86 +++++++++++++++++ src/alias/AliasModel.java | 7 ++ src/alias/AliasNameEditor.java | 89 +++++++++-------- 8 files changed, 289 insertions(+), 85 deletions(-) create mode 100644 src/alias/AliasFactory.java create mode 100644 src/alias/AliasIdentifierEditor.java diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 0634c434..1c6ddb87 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -166,32 +166,6 @@ public class Alias return !mAliasActions.isEmpty(); } - public boolean hasAudioType() - { - for( AliasID id: mAliasIDs ) - { - if( id.hasAudioType() ) - { - return true; - } - } - - return false; - } - - public AudioType getAudioType() - { - for( AliasID id: mAliasIDs ) - { - if( id.hasAudioType() ) - { - return id.getAudioType(); - } - } - - return AudioType.NORMAL; - } - /** * Returns the priority level of this alias, if defined, or the default priority */ diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index 65844e57..dbe45414 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -206,7 +206,7 @@ public class AliasController extends JPanel if( selected != null ) { -// addAlias( selected.copyOf() ); + addAlias( AliasFactory.copyOf( selected ) ); } break; case DELETE_ALIAS: diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 38e4c78d..28e6ecb6 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -43,6 +43,7 @@ public class AliasEditor extends JPanel implements ActionListener, Listener EMPTY_MODEL = new DefaultListModel<>(); + private JList mAliasIDList = new JList<>( EMPTY_MODEL ); + + private Alias mAlias; + + public AliasIdentifierEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[grow,fill][][grow,fill]" ) ); + + mAliasIDList.setVisibleRowCount( 6 ); + mAliasIDList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mAliasIDList.setLayoutOrientation( JList.VERTICAL ); + + JScrollPane scroller = new JScrollPane( mAliasIDList ); + add( scroller, "span,grow" ); + + add( new JButton( "New" ) ); + add( new JButton( "Copy" ) ); + add( new JButton( "Delete" ), "wrap" ); + } + + @Override + public void setAlias( Alias alias ) + { + mAlias = alias; + + if( mAlias == null || mAlias.getId().isEmpty() ) + { + mAliasIDList.setModel( EMPTY_MODEL ); + } + else + { + DefaultListModel model = new DefaultListModel(); + + List ids = mAlias.getId(); + + Collections.sort( ids, new Comparator() + { + @Override + public int compare( AliasID o1, AliasID o2 ) + { + return o1.toString().compareTo( o2.toString() ); + } + } ); + + for( AliasID id: ids ) + { + model.addElement( id ); + } + + mAliasIDList.setModel( model ); + } + } + + @Override + public void save() + { + // TODO Auto-generated method stub + + } +} diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index 4a1d11b1..3206a57e 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -270,6 +270,13 @@ public class AliasModel implements TableModel public void broadcast( AliasEvent event ) { mAliasEventBroadcaster.broadcast( event ); + + if( event.getEvent() == Event.CHANGE ) + { + int index = mAliases.indexOf( event.getAlias() ); + + broadcast( new TableModelEvent( this, index ) ); + } } public void addAliasEventListener( Listener listener ) diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index 742fde37..69768e3a 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -13,7 +13,6 @@ import javax.swing.JButton; import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JTextField; import map.IconManager; @@ -30,11 +29,9 @@ public class AliasNameEditor extends AliasConfigurationEditor private JComboBox mListCombo = new JComboBox<>( EMPTY_MODEL ); private JComboBox mGroupCombo = new JComboBox<>( EMPTY_MODEL ); + private JComboBox mMapIconCombo; private JTextField mName; - - private JLabel mLabelMapColor; private JButton mButtonColor; - private JComboBox mComboMapIcon; private JButton mBtnIconManager; private Alias mAlias; @@ -50,10 +47,33 @@ public class AliasNameEditor extends AliasConfigurationEditor private void init() { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", "[][][][][][grow][]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", + "[][][][][][][grow]" ) ); add( new JLabel( "List:" ) ); mListCombo.setEditable( true ); + mListCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + if( mListCombo.getSelectedItem() != null ) + { + List groups = mAliasModel + .getGroupNames( (String)mListCombo.getSelectedItem() ); + + if( groups.isEmpty() ) + { + mGroupCombo.setModel( EMPTY_MODEL ); + } + else + { + mGroupCombo.setModel( new DefaultComboBoxModel( + groups.toArray( new String[ groups.size() ] ) ) );; + } + } + } + } ); add( mListCombo, "wrap" ); add( new JLabel( "Group:" ) ); @@ -88,13 +108,13 @@ public class AliasNameEditor extends AliasConfigurationEditor add( new JLabel( "Icon:" ) ); - mComboMapIcon = new JComboBox( mSettingsManager.getMapIcons() ); + mMapIconCombo = new JComboBox( mSettingsManager.getMapIcons() ); MapIconListCellRenderer renderer = new MapIconListCellRenderer(); renderer.setPreferredSize( new Dimension( 200, 30 ) ); - mComboMapIcon.setRenderer( renderer ); + mMapIconCombo.setRenderer( renderer ); - add( mComboMapIcon, "wrap" ); + add( mMapIconCombo, "wrap" ); //Dummy place holder add( new JLabel() ); @@ -175,7 +195,7 @@ public class AliasNameEditor extends AliasConfigurationEditor if( savedIcon != null ) { - mComboMapIcon.setSelectedItem( savedIcon ); + mMapIconCombo.setSelectedItem( savedIcon ); } } else @@ -192,41 +212,26 @@ public class AliasNameEditor extends AliasConfigurationEditor @Override public void save() { - // TODO Auto-generated method stub - String alias = mName.getText(); - - if( alias != null ) + if( mAlias != null ) { -// boolean expanded = mAliasNode.getModel().getTree() -// .isExpanded( new TreePath( mAliasNode ) ); -// -// mAliasNode.getAlias().setName( alias ); -// ((ConfigurableNode)mAliasNode.getParent()).sort(); -// -// mAliasNode.getAlias().setColor( -// mButtonColor.getBackground().getRGB() ); - - MapIcon selectedIcon = (MapIcon)mComboMapIcon.getSelectedItem(); + if( mListCombo.getSelectedItem() != null ) + { + mAlias.setList( (String)mListCombo.getSelectedItem() ); + } -// if( selectedIcon != null ) -// { -// mAliasNode.getAlias().setIconName( selectedIcon.getName() ); -// } -// -// mAliasNode.save(); -// -// mAliasNode.show(); -// -// if( expanded ) -// { -// mAliasNode.getModel().getTree() -// .expandPath( new TreePath( mAliasNode ) ); -// } + if( mGroupCombo.getSelectedItem() != null ) + { + mAlias.setGroup( (String)mGroupCombo.getSelectedItem() ); + } + + mAlias.setName( mName.getText() ); + + mAlias.setColor( mButtonColor.getBackground().getRGB() ); + + if( mMapIconCombo.getSelectedItem() != null ) + { + mAlias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); + } } - else - { - JOptionPane.showMessageDialog( AliasNameEditor.this, "Please enter an alias name" ); - } - } } From 12ff5513b7e3767955730fbf48aade7a507d417f Mon Sep 17 00:00:00 2001 From: denny Date: Wed, 27 Jan 2016 19:18:31 -0500 Subject: [PATCH 10/74] Adds preliminary support for legacy P25 CAI VSELP voice frames. VSELP 1 and 2 messages simply capture the message at this point -- don't yet know the format for the voice frames and embedded signalling. --- src/module/decode/p25/P25MessageFramer.java | 10 +++++ .../p25/message/vselp/VSELP1Message.java | 43 +++++++++++++++++++ .../p25/message/vselp/VSELP2Message.java | 42 ++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/module/decode/p25/message/vselp/VSELP1Message.java create mode 100644 src/module/decode/p25/message/vselp/VSELP2Message.java diff --git a/src/module/decode/p25/P25MessageFramer.java b/src/module/decode/p25/P25MessageFramer.java index 227eba43..84ed4154 100644 --- a/src/module/decode/p25/P25MessageFramer.java +++ b/src/module/decode/p25/P25MessageFramer.java @@ -17,6 +17,8 @@ import module.decode.p25.message.tdu.lc.TDULCMessageFactory; import module.decode.p25.message.tdu.lc.TDULinkControlMessage; import module.decode.p25.message.tsbk.TSBKMessage; import module.decode.p25.message.tsbk.TSBKMessageFactory; +import module.decode.p25.message.vselp.VSELP1Message; +import module.decode.p25.message.vselp.VSELP2Message; import module.decode.p25.reference.DataUnitID; import org.slf4j.Logger; @@ -630,6 +632,14 @@ public class P25MessageFramer implements Listener mComplete = true; break; + case VSELP1: + mComplete = true; + dispatch( new VSELP1Message( mMessage.copy(), mDUID, mAliasList ) ); + break; + case VSELP2: + mComplete = true; + dispatch( new VSELP2Message( mMessage.copy(), mDUID, mAliasList ) ); + break; case UNKN: mComplete = true; dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) ); diff --git a/src/module/decode/p25/message/vselp/VSELP1Message.java b/src/module/decode/p25/message/vselp/VSELP1Message.java new file mode 100644 index 00000000..f2ad1113 --- /dev/null +++ b/src/module/decode/p25/message/vselp/VSELP1Message.java @@ -0,0 +1,43 @@ +package module.decode.p25.message.vselp; + +import alias.AliasList; +import bits.BinaryMessage; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; +import module.decode.p25.reference.Vendor; + +public class VSELP1Message extends P25Message +{ + /** + * Motorola VSELP audio message 1. + * + * @param message + * @param duid + * @param aliasList + */ + public VSELP1Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( getMessageStub() ); + sb.append( " " ); + sb.append( mMessage.toString() ); + + return sb.toString(); + } + + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "VSELP 1 VOICE FRAME" ); + + return sb.toString(); + } +} diff --git a/src/module/decode/p25/message/vselp/VSELP2Message.java b/src/module/decode/p25/message/vselp/VSELP2Message.java new file mode 100644 index 00000000..e6e1b1c6 --- /dev/null +++ b/src/module/decode/p25/message/vselp/VSELP2Message.java @@ -0,0 +1,42 @@ +package module.decode.p25.message.vselp; + +import alias.AliasList; +import bits.BinaryMessage; +import module.decode.p25.message.P25Message; +import module.decode.p25.reference.DataUnitID; + +public class VSELP2Message extends P25Message +{ + /** + * Motorola VSELP audio message 1. + * + * @param message + * @param duid + * @param aliasList + */ + public VSELP2Message( BinaryMessage message, DataUnitID duid, AliasList aliasList ) + { + super( message, duid, aliasList ); + } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( getMessageStub() ); + sb.append( " " ); + sb.append( mMessage.toString() ); + + return sb.toString(); + } + + public String getMessageStub() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "VSELP 2 VOICE FRAME" ); + + return sb.toString(); + } +} From 37a93c8884c4fa309e7cfe963d08796327c254bb Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 13 Feb 2016 11:54:06 -0500 Subject: [PATCH 11/74] Merges master onto development branch --- src/audio/AudioManager.java | 30 +++++++++++++++++++++++++++--- src/gui/SDRTrunk.java | 2 +- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index 9d21bf98..b8e1e183 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -73,6 +73,7 @@ public class AudioManager implements Listener, IAudioController private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mProcessingTask; + private MixerManager mMixerManager; private MixerChannelConfiguration mMixerChannelConfiguration; @@ -81,9 +82,10 @@ public class AudioManager implements Listener, IAudioController * packets to any combination of outputs based on any alias audio routing * options specified by the user. */ - public AudioManager( ThreadPoolManager manager ) + public AudioManager( ThreadPoolManager manager, MixerManager mixerManager ) { mThreadPoolManager = manager; + mMixerManager = mixerManager; loadSettings(); } @@ -100,8 +102,7 @@ public class AudioManager implements Listener, IAudioController String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() ); - MixerChannelConfiguration[] mixerConfigurations = - MixerManager.getInstance().getOutputMixers(); + MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) { @@ -295,6 +296,29 @@ public class AudioManager implements Listener, IAudioController mLowestPriorityConnection = null; } + /** + * Clears all channel assignments and terminates all audio outputs in + * preparation for complete shutdown or change to another mixer/channel + * configuration + */ + private void disposeCurrentConfiguration() + { + mChannelConnectionMap.clear(); + + for( AudioOutputConnection connection : mAudioOutputConnections ) + { + connection.dispose(); + } + + mAvailableConnectionCount = 0; + + mAudioOutputConnections.clear(); + + mAudioOutputMap.clear(); + + mLowestPriorityConnection = null; + } + @Override public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException { diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 473ea3e7..a81ebe6e 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -140,7 +140,7 @@ public class SDRTrunk AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); channelProcessingManager.addMessageListener( aliasActionManager ); - AudioManager audioManager = new AudioManager( threadPoolManager ); + AudioManager audioManager = new AudioManager( threadPoolManager, sourceManager.getMixerManager() ); channelProcessingManager.addAudioPacketListener( audioManager ); MapService mapService = new MapService( mSettingsManager ); From 5071205e73a611195b0180225fafbb3d388d1554 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 13 Feb 2016 13:59:10 -0500 Subject: [PATCH 12/74] Updates. --- src/module/decode/DecoderFactory.java | 3 +-- .../decode/state/ChannelStatePanel.java | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 109c0017..4b5dba82 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -210,7 +210,6 @@ public class DecoderFactory if( channelType == ChannelType.STANDARD ) { - long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds modules.add( new TrafficChannelManager( channelModel, channelProcessingManager, decodeConfig, recordConfig, channel.getSystem(), @@ -256,7 +255,7 @@ public class DecoderFactory if( channelType == ChannelType.STANDARD ) { //Set call timeout to 3 seconds for P25 traffic channels - long timeout = 3000; + timeout = 3000; modules.add( new TrafficChannelManager( channelModel, channelProcessingManager, decodeConfig, diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java index 023d6c52..9234d976 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelStatePanel.java @@ -260,6 +260,7 @@ public class ChannelStatePanel extends JPanel if( channelState != null ) { +<<<<<<< Upstream, based on origin/master switch( channelState.getState() ) { case CALL: @@ -284,6 +285,24 @@ public class ChannelStatePanel extends JPanel if( p == null ) { p = getGradient( mColorTopIdle, mColorMiddleIdle ); +======= + case CALL: + p = getGradient( mColorTopCall, mColorMiddleCall ); + break; + case CONTROL: + p = getGradient( mColorTopControl, mColorMiddleControl ); + break; + case DATA: + p = getGradient( mColorTopData, mColorMiddleData ); + break; + case FADE: + p = getGradient( mColorTopFade, mColorMiddleFade ); + break; + case IDLE: + default: + p = getGradient( mColorTopIdle, mColorMiddleIdle ); + break; +>>>>>>> 0e01381 Updates. } g2.setPaint( p ); From 3f674c0b8f12de36bb46fcd693e274bcfd13ff09 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 13 Feb 2016 14:06:53 -0500 Subject: [PATCH 13/74] Revert "Updates." This reverts commit 0e01381935f0630fa8e1755d7c9c7fbeed7ae746. --- src/module/decode/DecoderFactory.java | 1 + src/module/decode/state/ChannelStatePanel.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 4b5dba82..02a41441 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -205,6 +205,7 @@ public class DecoderFactory modules.add( new MPT1327Decoder( aliasList, sync ) ); + modules.add( new MPT1327DecoderState( aliasList, channelMap, channelType, mptConfig.getCallTimeout() * 1000 ) ); diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java index 9234d976..e133989f 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelStatePanel.java @@ -298,6 +298,7 @@ public class ChannelStatePanel extends JPanel case FADE: p = getGradient( mColorTopFade, mColorMiddleFade ); break; + case END: case IDLE: default: p = getGradient( mColorTopIdle, mColorMiddleIdle ); From 1d7f2557840a8537c39eb5b90af537808eb6a143 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 13 Feb 2016 14:14:54 -0500 Subject: [PATCH 14/74] Revert "Merges master onto development branch" This reverts commit 2420bc901c63ad9d41e7c99ac95a426f826182ff. --- .gitignore | 3 +- README.md | 31 +- build/build.xml | 114 ++-- scripts/run_sdrtrunk_windows.bat | 2 +- src/audio/AudioChannelPanel.java | 108 ++-- src/audio/AudioManager.java | 608 +++++++----------- src/audio/output/AudioOutput.java | 36 +- src/audio/output/MonoAudioOutput.java | 155 ++--- src/audio/output/StereoAudioOutput.java | 139 ++-- src/bits/MessageFramer.java | 10 - src/controller/ThreadPoolManager.java | 24 +- src/gui/SDRTrunk.java | 4 +- src/message/Message.java | 96 +-- src/module/decode/event/CallEvent.java | 5 - .../decode/event/MessageActivityModel.java | 3 +- .../decode/event/MessageDetailsProvider.java | 80 +++ src/module/decode/ltrnet/LTRCallEvent.java | 17 +- src/module/decode/ltrnet/LTRNetDecoder.java | 1 - .../ltrstandard/LTRStandardDecoder.java | 1 - .../ltrstandard/LTRStandardDecoderState.java | 476 ++++++-------- .../ltrstandard/LTRStandardISWMessage.java | 238 +++++++ .../ltrstandard/LTRStandardMessage.java | 254 ++++++++ .../ltrstandard/LTRStandardMessageFilter.java | 1 - .../LTRStandardMessageProcessor.java | 78 +-- .../ltrstandard/LTRStandardOSWMessage.java | 248 +++++++ .../ltrstandard/message/CallEndMessage.java | 67 -- .../ltrstandard/message/CallMessage.java | 68 -- .../ltrstandard/message/IdleMessage.java | 58 -- .../message/LTRStandardMessage.java | 213 ------ .../ltrstandard/message/UnknownMessage.java | 58 -- src/module/decode/mpt1327/MPT1327Decoder.java | 4 +- .../decode/mpt1327/MPT1327DecoderState.java | 68 +- src/module/decode/p25/P25DecoderState.java | 275 ++++---- src/module/decode/p25/message/P25Message.java | 2 - .../p25/message/pdu/PDUMessageFactory.java | 4 +- .../confirmed/PDUConfirmedMessageFactory.java | 20 +- .../message/pdu/confirmed/PDUTypeUnknown.java | 55 -- .../p25/message/pdu/confirmed/PacketData.java | 66 -- .../confirmed/SNDCPDeactivateTDSContext.java | 2 +- .../p25/message/tsbk/TSBKMessageFactory.java | 7 +- .../osp/control/IdentifierUpdateTDMA.java | 159 ----- .../tsbk/osp/control/SyncBroadcast.java | 220 ++----- src/module/decode/p25/reference/Opcode.java | 2 +- src/module/decode/p25/reference/PDUType.java | 20 +- .../p25/reference/ServiceAccessPoint.java | 2 +- .../decode/passport/PassportDecoder.java | 1 - .../state/ChangeChannelTimeoutEvent.java | 29 - src/module/decode/state/ChannelList.java | 23 +- src/module/decode/state/ChannelState.java | 507 +++++++-------- .../decode/state/DecoderStateEvent.java | 1 - src/module/decode/state/State.java | 59 +- src/org/jdesktop/swingx/JXMapViewer.java | 9 +- src/properties/SystemProperties.java | 23 +- src/settings/ColorSetting.java | 2 +- src/settings/SettingsManager.java | 27 +- .../mixer/MixerChannelConfiguration.java | 8 - src/source/tuner/TunerChannel.java | 8 +- src/spectrum/DFTProcessor.java | 18 +- src/spectrum/SpectralDisplayPanel.java | 37 -- src/spectrum/WaterfallPanel.java | 2 - 60 files changed, 2106 insertions(+), 2750 deletions(-) create mode 100644 src/module/decode/event/MessageDetailsProvider.java create mode 100644 src/module/decode/ltrstandard/LTRStandardISWMessage.java create mode 100644 src/module/decode/ltrstandard/LTRStandardMessage.java create mode 100644 src/module/decode/ltrstandard/LTRStandardOSWMessage.java delete mode 100644 src/module/decode/ltrstandard/message/CallEndMessage.java delete mode 100644 src/module/decode/ltrstandard/message/CallMessage.java delete mode 100644 src/module/decode/ltrstandard/message/IdleMessage.java delete mode 100644 src/module/decode/ltrstandard/message/LTRStandardMessage.java delete mode 100644 src/module/decode/ltrstandard/message/UnknownMessage.java delete mode 100644 src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java delete mode 100644 src/module/decode/p25/message/pdu/confirmed/PacketData.java delete mode 100644 src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java delete mode 100644 src/module/decode/state/ChangeChannelTimeoutEvent.java diff --git a/.gitignore b/.gitignore index 7be9efe9..7599d847 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ /*.class /classes* -/product* -/bin/ +/product* \ No newline at end of file diff --git a/README.md b/README.md index 22bcc018..8c1e9dc4 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,19 @@ -# sdrtrunk # -A cross-platform java application for decoding, monitoring and recording trunked -mobile and related radio protocols using Software Defined Radios (SDR). +# sdrtrunk +A cross-platform java application for decoding, monitoring, recording and streaming trunked mobile and related radio protocols using Software Defined Radios (SDR). -# Documentation # * [Getting Started](https://github.com/DSheirer/sdrtrunk/wiki/GettingStarted) -* [User's Manual (Version 0.2.0)](https://github.com/DSheirer/sdrtrunk/wiki/UserManual) -* [Wiki](https://github.com/DSheirer/sdrtrunk/wiki) - -# Support Group and Error Reporting # -You can get help with setting up the sdrtrunk application or report any errors -that you encounter while running the application in the sdrtrunk Google Group at: - +* [User's Manual](https://github.com/DSheirer/sdrtrunk/wiki/UserManual) +* [Download](https://drive.google.com/folderview?id=0B7BHsssXUq8eOHBTNndCczZTd0k&usp=sharing) * [Support](https://groups.google.com/forum/#!forum/sdrtrunk) -# Updates # -Subscribe to this group to receive updates about new releases and the ongoing -application development effort. - -* [Subscribe for Updates](https://groups.google.com/forum/#!forum/sdrtrunk) - ![](https://github.com/DSheirer/sdrtrunk/wiki/images/SDRTrunk_application.png) + diff --git a/build/build.xml b/build/build.xml index 4cdb3eaa..3667c7ff 100644 --- a/build/build.xml +++ b/build/build.xml @@ -7,115 +7,113 @@ - - - - - - - - - - - + - - - - - - + + + + + + + + + - + - + - + - - + + + - + - + - - - + - + - - - + + + - + - - + + - - - + + + - - - - + + - - + + + + + + + + - - - - - - - - - - - - + - + + + + + + + + + + - - + + \ No newline at end of file diff --git a/scripts/run_sdrtrunk_windows.bat b/scripts/run_sdrtrunk_windows.bat index 27c0b763..1206e039 100644 --- a/scripts/run_sdrtrunk_windows.bat +++ b/scripts/run_sdrtrunk_windows.bat @@ -1 +1 @@ -java -XX:+UseG1GC -cp "sdrtrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk \ No newline at end of file +java -XX:+UseG1GC -cp "SDRTrunk.jar;*;libs/*;config/*;images/*" gui.SDRTrunk \ No newline at end of file diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java index d23c2024..298e6b18 100644 --- a/src/audio/AudioChannelPanel.java +++ b/src/audio/AudioChannelPanel.java @@ -48,11 +48,8 @@ public class AudioChannelPanel extends JPanel private JLabel mFromLabel = new JLabel( "FROM:" ); private JLabel mFrom = new JLabel( "" ); private JLabel mFromAlias = new JLabel( "" ); - - private enum AudioStatus { STARTED,STOPPED }; - private AudioStatus mAudioStatus = AudioStatus.STOPPED; - private AudioMetadata mAudioMetadata; + private boolean mConfigured = false; public AudioChannelPanel( SettingsManager settingsManager, AudioOutput audioOutput ) { @@ -137,19 +134,15 @@ public class AudioChannelPanel extends JPanel { switch( audioEvent.getType() ) { - case AUDIO_STARTED: - synchronized( mAudioStatus ) - { - mAudioStatus = AudioStatus.STARTED; - updateLabels(); - } - break; case AUDIO_STOPPED: - synchronized( mAudioStatus ) + EventQueue.invokeLater( new Runnable() { - mAudioStatus = AudioStatus.STOPPED; - resetLabels(); - } + @Override + public void run() + { + resetLabels(); + } + } ); break; case AUDIO_MUTED: case AUDIO_UNMUTED: @@ -173,55 +166,13 @@ public class AudioChannelPanel extends JPanel */ private void resetLabels() { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - mFrom.setText( "" ); - updateAlias( mFromAlias, null ); - - mTo.setText( "" ); - updateAlias( mToAlias, null ); - } - } ); - } - - private void updateLabels() - { - if( mAudioMetadata != null ) - { - final Metadata from = mAudioMetadata.getMetadata( MetadataType.FROM ); - - final Metadata to = mAudioMetadata.getMetadata( MetadataType.TO ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - if( from != null ) - { - mFrom.setText( from.getValue() ); - updateAlias( mFromAlias, from.getAlias() ); - } - else - { - mFrom.setText( "-----" ); - } - - if( to != null ) - { - mTo.setText( to.getValue() ); - updateAlias( mToAlias, to.getAlias() ); - } - else - { - mTo.setText( "-----" ); - } - } - } ); - } + mFrom.setText( "" ); + updateAlias( mFromAlias, null ); + + mTo.setText( "" ); + updateAlias( mToAlias, null ); + + mConfigured = false; } /** @@ -263,17 +214,32 @@ public class AudioChannelPanel extends JPanel @Override public void receive( AudioMetadata audioMetadata ) { - mAudioMetadata = audioMetadata; - - if( audioMetadata.isUpdated() ) + if( !mConfigured || audioMetadata.isUpdated() ) { - synchronized( mAudioStatus ) + final Metadata from = audioMetadata.getMetadata( MetadataType.FROM ); + + final Metadata to = audioMetadata.getMetadata( MetadataType.TO ); + + EventQueue.invokeLater( new Runnable() { - if( mAudioStatus == AudioStatus.STARTED ) + @Override + public void run() { - updateLabels(); + if( from != null ) + { + mFrom.setText( from.getValue() ); + updateAlias( mFromAlias, from.getAlias() ); + } + + if( to != null ) + { + mTo.setText( to.getValue() ); + updateAlias( mToAlias, to.getAlias() ); + } } - } + } ); + + mConfigured = true; } } } diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index b8e1e183..6fa1a9f7 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -2,7 +2,7 @@ package audio; /******************************************************************************* * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014,2015 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 @@ -17,12 +17,16 @@ package audio; * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ + import java.util.ArrayList; + import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -33,12 +37,10 @@ import javax.sound.sampled.Mixer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import properties.SystemProperties; import sample.Broadcaster; import sample.Listener; import source.mixer.MixerChannel; import source.mixer.MixerChannelConfiguration; -import source.mixer.MixerManager; import audio.AudioEvent.Type; import audio.output.AudioOutput; import audio.output.MonoAudioOutput; @@ -51,9 +53,7 @@ public class AudioManager implements Listener, IAudioController private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); public static final int AUDIO_TIMEOUT = 2000; //2 seconds - - public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; - public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; + public static final int NO_SOURCE = 0; public static final AudioEvent CONFIGURATION_CHANGE_STARTED = new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null ); @@ -62,90 +62,49 @@ public class AudioManager implements Listener, IAudioController new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null ); private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); - private Map mChannelConnectionMap = new HashMap<>(); - private List mAudioOutputConnections = new ArrayList<>(); - private AudioOutputConnection mLowestPriorityConnection; - private int mAvailableConnectionCount; + private Map mChannelAssignments = new HashMap<>(); + private int mLowestPrioritySource; + private List mAvailableAudioOutputs = new ArrayList<>(); private Map mAudioOutputMap = new HashMap<>(); + private List mChannelNames = new ArrayList<>(); private Broadcaster mControllerBroadcaster = new Broadcaster<>(); private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mProcessingTask; - private MixerManager mMixerManager; - private MixerChannelConfiguration mMixerChannelConfiguration; + private MixerChannelConfiguration mMixerChannel; /** * Processes all audio produced by the decoding channels and routes audio * packets to any combination of outputs based on any alias audio routing * options specified by the user. */ - public AudioManager( ThreadPoolManager manager, MixerManager mixerManager ) + public AudioManager( ThreadPoolManager manager ) { mThreadPoolManager = manager; - mMixerManager = mixerManager; - loadSettings(); - } - - private void loadSettings() - { - MixerChannelConfiguration configuration = null; + //TODO: save audio system preferences to the settings manager - SystemProperties properties = SystemProperties.getInstance(); + /* Use the system default mixer and mono channel as default startup */ + Mixer mixer = AudioSystem.getMixer( null ); - Mixer defaultMixer = AudioSystem.getMixer( null ); - - String mixer = properties.get( AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName() ); - - String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() ); - - MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); - - for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) + if( mixer != null ) { - if( mixerConfig.matches( mixer, channels ) ) - { - configuration = mixerConfig; - } - } - - if( configuration == null ) - { - configuration = getDefaultConfiguration(); - } - - try - { - setMixerChannelConfiguration( configuration ); - } - catch( Exception e ) - { - mLog.error( "Couldn't set stored audio mixer/channel configuration" - + " - using default", e ); + mMixerChannel = new MixerChannelConfiguration( mixer, MixerChannel.MONO ); try { - setMixerChannelConfiguration( getDefaultConfiguration() ); - } - catch( Exception e2 ) + setMixerChannelConfiguration( mMixerChannel ); + } + catch ( AudioException e ) { - mLog.error( "Couldn't set default audio mixer/channel " - + "configuration - no audio will be available", e2 ); + mLog.error( "Couldn't create an audio output device", e ); } } } - private MixerChannelConfiguration getDefaultConfiguration() - { - /* Use the system default mixer and mono channel as default startup */ - Mixer defaultMixer = AudioSystem.getMixer( null ); - - return new MixerChannelConfiguration( defaultMixer, MixerChannel.MONO ); - } - public void dispose() { if( mThreadPoolManager != null && mProcessingTask != null ) @@ -157,15 +116,6 @@ public class AudioManager implements Listener, IAudioController mThreadPoolManager = null; mProcessingTask = null; - - mChannelConnectionMap.clear(); - - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - connection.dispose(); - } - - mAudioOutputConnections.clear(); } @Override @@ -174,44 +124,207 @@ public class AudioManager implements Listener, IAudioController mAudioPacketQueue.add( packet ); } - /** - * Checks each audio channel assignment and disconnects any inactive connections - */ - private void disconnectInactiveChannelAssignments() + public class AudioPacketProcessor implements Runnable { - boolean changed = false; - - for( AudioOutputConnection connection: mAudioOutputConnections ) + @Override + public void run() { - if( connection.isInactive() && - mChannelConnectionMap.containsKey( connection.getSource() ) ) + removeCompletedChannelAssignments(); + + if( mAudioPacketQueue != null ) { - mChannelConnectionMap.remove( connection.getSource() ); - connection.disconnect(); - changed = true; + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo( packets ); + + for( AudioPacket packet: packets ) + { + /* Don't process any packet's marked as do not monitor */ + if( !packet.getAudioMetadata().isDoNotMonitor() ) + { + int source = packet.getAudioMetadata().getSource(); + + if( packet.getType() != AudioPacket.Type.END ) + { + /* Existing channel assignment */ + if( mChannelAssignments.containsKey( source ) ) + { + mChannelAssignments.get( source ).getAudioOutput() + .receive( packet ); + } + /* Start a new channel assignment */ + else if( !mAvailableAudioOutputs.isEmpty() ) + { + AudioOutput channel = mAvailableAudioOutputs.remove( 0 ); + + mChannelAssignments.put( packet.getAudioMetadata().getSource(), + new AudioChannelAssignment( channel, packet.getAudioMetadata().getPriority() ) ); + + updateLowestPriorityAssignment(); + + channel.receive( packet ); + } + /* Override the lowest priority assignment */ + else + { + if( mLowestPrioritySource > -1 ) + { + int priority = packet.getAudioMetadata().getPriority(); + + if( packet.getAudioMetadata().isSelected() || + priority < mChannelAssignments + .get( mLowestPrioritySource ).getPriority() ) + { + AudioChannelAssignment assignment = + mChannelAssignments.remove( mLowestPrioritySource ); + + if( assignment != null ) + { + assignment.setPriority( priority ); + + int overrideSource = packet.getAudioMetadata().getSource(); + + mChannelAssignments.put( overrideSource, assignment ); + + mLowestPrioritySource = overrideSource; + + assignment.getAudioOutput().receive( packet ); + } + else + { + mLowestPrioritySource = -1; + } + } + } + } + } + } + } } } - - if( changed ) + } + + /** + * Checks each audio channel assignment and removes any completed channels. + */ + private void removeCompletedChannelAssignments() + { + if( !mChannelAssignments.isEmpty() ) { - updateLowestPriorityAssignment(); + Iterator> it = + mChannelAssignments.entrySet().iterator(); + + Entry entry; + + boolean removed = false; + + while( it.hasNext() ) + { + entry = it.next(); + + if( entry.getValue().isComplete() ) + { + mAvailableAudioOutputs.add( entry.getValue().getAudioOutput() ); + entry.getValue().dispose(); + it.remove(); + removed = true; + } + } + + if( removed ) + { + updateLowestPriorityAssignment(); + } } } /** - * Identifies the lowest priority channel connection + * Identifies the lowest priority channel assignment ... the one that can + * be overridden with a higher priority call */ private void updateLowestPriorityAssignment() { - mLowestPriorityConnection = null; + int lowestPriority = -1; - for( AudioOutputConnection connection: mAudioOutputConnections ) + mLowestPrioritySource = -1; + + for( int source: mChannelAssignments.keySet() ) { - if( connection.isConnected() && - ( mLowestPriorityConnection == null || - mLowestPriorityConnection.getPriority() > connection.getPriority() ) ) + AudioChannelAssignment assignment = mChannelAssignments.get( source ); + + if( assignment.getPriority() > lowestPriority ) { - mLowestPriorityConnection = connection; + lowestPriority = assignment.getPriority(); + + mLowestPrioritySource = source; + } + } + } + + public void endAllAudioAssignments() + { + for( AudioOutput output: mAudioOutputMap.values() ) + { + output.receive( new AudioPacket( AudioPacket.Type.END, null ) ); + } + } + + public class AudioChannelAssignment implements Listener + { + private static final long CALL_END_DELAY = 2000; //2 seconds + + private AudioOutput mAudioOutput; + private int mPriority; + private long mAudioStoppedTime = System.currentTimeMillis(); + + public AudioChannelAssignment( AudioOutput audioOutput, int priority ) + { + mAudioOutput = audioOutput; + mPriority = priority; + + mAudioOutput.addAudioEventListener( this ); + } + + public boolean isComplete() + { + return mAudioStoppedTime != Long.MAX_VALUE && + mAudioStoppedTime + CALL_END_DELAY < System.currentTimeMillis(); + } + + public void dispose() + { + mAudioOutput.removeAudioEventListener( this ); + mAudioOutput.reset(); + mAudioOutput = null; + } + + public AudioOutput getAudioOutput() + { + return mAudioOutput; + } + + public int getPriority() + { + return mPriority; + } + + public void setPriority( int priority ) + { + mPriority = priority; + } + + @Override + public void receive( AudioEvent event ) + { + switch( event.getType() ) + { + case AUDIO_STARTED: + case AUDIO_STOPPED: + case AUDIO_CONTINUATION: + mAudioStoppedTime = System.currentTimeMillis(); + break; + default: + break; } } } @@ -229,29 +342,41 @@ public class AudioManager implements Listener, IAudioController { mThreadPoolManager.cancel( mProcessingTask ); } + + endAllAudioAssignments(); - disposeCurrentConfiguration(); + for( Entry e: mChannelAssignments.entrySet() ) + { + e.getValue().getAudioOutput().dispose(); + } + mChannelAssignments.clear(); + for( AudioOutput output: mAvailableAudioOutputs ) + { + output.dispose(); + } + mAvailableAudioOutputs.clear(); + + mChannelNames.clear(); + mAudioOutputMap.clear(); + switch( entry.getMixerChannel() ) { case MONO: - AudioOutput mono = new MonoAudioOutput( mThreadPoolManager, - entry.getMixer() ); - mAudioOutputConnections.add( new AudioOutputConnection( mono ) ); - mAvailableConnectionCount++; + AudioOutput mono = new MonoAudioOutput( entry.getMixer() ); + mAvailableAudioOutputs.add( mono ); + mChannelNames.add( mono.getChannelName() ); mAudioOutputMap.put( mono.getChannelName(), mono ); break; case STEREO: - AudioOutput left = new StereoAudioOutput( mThreadPoolManager, - entry.getMixer(), MixerChannel.LEFT ); - mAudioOutputConnections.add( new AudioOutputConnection( left ) ); - mAvailableConnectionCount++; + AudioOutput left = new StereoAudioOutput( entry.getMixer(), MixerChannel.LEFT ); + mAvailableAudioOutputs.add( left ); + mChannelNames.add( left.getChannelName() ); mAudioOutputMap.put( left.getChannelName(), left ); - AudioOutput right = new StereoAudioOutput( mThreadPoolManager, - entry.getMixer(), MixerChannel.RIGHT ); - mAudioOutputConnections.add( new AudioOutputConnection( right ) ); - mAvailableConnectionCount++; + AudioOutput right = new StereoAudioOutput( entry.getMixer(), MixerChannel.RIGHT ); + mAvailableAudioOutputs.add( right ); + mChannelNames.add( right.getChannelName() ); mAudioOutputMap.put( right.getChannelName(), right ); break; default: @@ -264,12 +389,6 @@ public class AudioManager implements Listener, IAudioController TimeUnit.MILLISECONDS ); mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_COMPLETE ); - - SystemProperties properties = SystemProperties.getInstance(); - - properties.set( AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName() ); - properties.set( AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name() ); - properties.save(); } } @@ -296,33 +415,10 @@ public class AudioManager implements Listener, IAudioController mLowestPriorityConnection = null; } - /** - * Clears all channel assignments and terminates all audio outputs in - * preparation for complete shutdown or change to another mixer/channel - * configuration - */ - private void disposeCurrentConfiguration() - { - mChannelConnectionMap.clear(); - - for( AudioOutputConnection connection : mAudioOutputConnections ) - { - connection.dispose(); - } - - mAvailableConnectionCount = 0; - - mAudioOutputConnections.clear(); - - mAudioOutputMap.clear(); - - mLowestPriorityConnection = null; - } - @Override public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException { - return mMixerChannelConfiguration; + return mMixerChannel; } @Override @@ -353,244 +449,4 @@ public class AudioManager implements Listener, IAudioController { mControllerBroadcaster.removeListener( listener ); } - - /** - * Returns an audio output connection for the packet if one is - * available, or overrides an existing lower priority connection. - * Returns null if no connection is available for the audio packet. - * - * @param packet from a source - * @return an audio output connection or null - */ - private AudioOutputConnection getConnection( AudioPacket packet ) - { - int source = packet.getAudioMetadata().getSource(); - - //Use an existing connection - if( mChannelConnectionMap.containsKey( source ) ) - { - return mChannelConnectionMap.get( source ); - } - - //Connect to and use an available connection - if( mAvailableConnectionCount > 0 ) - { - for( AudioOutputConnection connection: mAudioOutputConnections ) - { - if( connection.isDisconnected() ) - { - connection.connect( source ); - mChannelConnectionMap.put( source, connection ); - return connection; - } - } - } - //Preempt an existing connection and connect it to this higher priority source - else //Check for a channel priority override - { - int priority = packet.getAudioMetadata().getPriority(); - - if( mLowestPriorityConnection != null && - priority < mLowestPriorityConnection.getPriority() ) - { - AudioOutputConnection connection = mLowestPriorityConnection; - - mChannelConnectionMap.remove( connection.getSource() ); - - connection.connect( source ); - - mChannelConnectionMap.put( source, connection ); - - updateLowestPriorityAssignment(); - - return connection; - } - } - - return null; - } - - public class AudioPacketProcessor implements Runnable - { - @Override - public void run() - { - try - { - disconnectInactiveChannelAssignments(); - - if( mAudioPacketQueue != null ) - { - List packets = new ArrayList(); - - mAudioPacketQueue.drainTo( packets ); - - for( AudioPacket packet: packets ) - { - /* Don't process any packet's marked as do not monitor */ - if( !packet.getAudioMetadata().isDoNotMonitor() && - packet.getType() == AudioPacket.Type.AUDIO ) - { - AudioOutputConnection connection = getConnection( packet ); - - if( connection != null ) - { - connection.receive( packet ); - } - } - } - } - } - catch( Exception e ) - { - mLog.error( "Encountered error while processing audio packets", e ); - } - } - } - - /** - * Audio output connection manages a connection between a source and an audio - * output and maintains current state information about the audio activity - * received form the source. - */ - public class AudioOutputConnection implements Listener - { - private static final long CALL_END_DELAY = 2000; //2 seconds - private static final int DISCONNECTED = -1; - - private AudioOutput mAudioOutput; - private int mPriority = 0; - private long mLastActivity = System.currentTimeMillis(); - private int mSource = DISCONNECTED; - - public AudioOutputConnection( AudioOutput audioOutput ) - { - mAudioOutput = audioOutput; - mAudioOutput.addAudioEventListener( this ); - } - - public void receive( AudioPacket packet ) - { - if( packet.hasAudioMetadata() && - packet.getAudioMetadata().getSource() == mSource ) - { - int priority = packet.getAudioMetadata().getPriority(); - - if( mPriority != priority ) - { - mPriority = priority; - updateLowestPriorityAssignment(); - } - - updateTimestamp(); - - if( mAudioOutput != null ) - { - mAudioOutput.receive( packet ); - } - } - else - { - if( packet.hasAudioMetadata() ) - { - mLog.error( "Received audio packet from channel source [" + - packet.getAudioMetadata().getSource() + - "] however this assignment is currently connected " - + "to channel source [" + mSource + "]" ); - } - else - { - mLog.error( "Received audio packet with no metadata - " - + "cannot route audio packet" ); - } - } - } - - private void updateTimestamp() - { - mLastActivity = System.currentTimeMillis(); - } - - /** - * Terminates the audio output and prepares this connection for disposal - */ - public void dispose() - { - mAudioOutput.dispose(); - mAudioOutput = null; - } - - /** - * Indicates if this assignment is currently disconnected from a channel source - */ - public boolean isDisconnected() - { - return mSource == DISCONNECTED; - } - - /** - * Indicates if this assignment is currently connected to a channel source - */ - public boolean isConnected() - { - return !isDisconnected(); - } - - /** - * Connects this assignment to the indicated source so that audio - * packets from this source can be sent to the audio output - */ - public void connect( int source ) - { - mSource = source; - updateTimestamp(); - } - - /** - * Currently connected source or -1 if disconnected - */ - public int getSource() - { - return mSource; - } - - /** - * Disconnects this assignment from the source and prevents any audio - * from being routed to the audio output until another source is assigned - */ - public void disconnect() - { - mSource = DISCONNECTED; - } - - /** - * Indicates if audio output is current inactive, meaning that the - * audio output hasn't recently processed any audio packets. - */ - public boolean isInactive() - { - return mLastActivity != Long.MAX_VALUE && - mLastActivity + CALL_END_DELAY < System.currentTimeMillis(); - } - - public int getPriority() - { - return mPriority; - } - - @Override - public void receive( AudioEvent event ) - { - switch( event.getType() ) - { - case AUDIO_STARTED: - case AUDIO_STOPPED: - case AUDIO_CONTINUATION: - updateTimestamp(); - break; - default: - break; - } - } - } } diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java index 74b2e822..d9506472 100644 --- a/src/audio/output/AudioOutput.java +++ b/src/audio/output/AudioOutput.java @@ -1,9 +1,10 @@ package audio.output; import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -11,26 +12,23 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; import audio.AudioEvent; +import audio.AudioEvent.Type; import audio.AudioPacket; import audio.metadata.AudioMetadata; -import controller.ThreadPoolManager; -public abstract class AudioOutput implements Listener, LineListener +public abstract class AudioOutput implements Listener { private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); + private AtomicBoolean mSquelched = new AtomicBoolean( false ); private Listener mAudioMetadataListener; + protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); protected LinkedTransferQueue mBuffer = new LinkedTransferQueue<>(); - protected ThreadPoolManager mThreadPoolManager; + protected ScheduledExecutorService mExecutorService; protected boolean mCanProcessAudio; protected long mLastActivity; - public AudioOutput( ThreadPoolManager threadPoolManager ) - { - mThreadPoolManager = threadPoolManager; - } - public void reset() { broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, @@ -43,11 +41,14 @@ public abstract class AudioOutput implements Listener, LineListener mBuffer.clear(); + if( mExecutorService != null ) + { + mExecutorService.shutdown(); + } + mAudioEventBroadcaster.dispose(); mAudioEventBroadcaster = null; mAudioMetadataListener = null; - - mThreadPoolManager = null; } public abstract String getChannelName(); @@ -71,6 +72,19 @@ public abstract class AudioOutput implements Listener, LineListener mAudioEventBroadcaster.broadcast( audioEvent ); } + public void setSquelched( boolean squelched ) + { + mSquelched.set( squelched ); + + broadcast( new AudioEvent( squelched ? Type.AUDIO_SQUELCHED : + Type.AUDIO_UNSQUELCHED, getChannelName() ) ); + } + + public boolean isSquelched() + { + return mSquelched.get(); + } + /** * Registers a single listener to receive audio start and audio stop events */ diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java index ec1f7816..76bf2324 100644 --- a/src/audio/output/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -5,14 +5,13 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; @@ -25,8 +24,8 @@ import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; import audio.AudioPacket.Type; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; +import audio.output.StereoAudioOutput.QueueProcessor; +import controller.NamingThreadFactory; /** * Mono Audio output with automatic flow control based on the availability of @@ -51,14 +50,14 @@ public class MonoAudioOutput extends AudioOutput private SourceDataLine mOutput; private FloatControl mGainControl; private BooleanControl mMuteControl; + private AudioEvent mAudioStartEvent; private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; - private ScheduledFuture mProcessorTask; - public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer ) + public MonoAudioOutput( Mixer mixer ) { - super( threadPoolManager ); + super(); mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, getChannelName() ); @@ -75,8 +74,15 @@ public class MonoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE ); - mOutput.addLineListener( this ); + mCanProcessAudio = true; + + mExecutorService = Executors.newSingleThreadScheduledExecutor( + new NamingThreadFactory( "audio (mono) output" ) ); + + /* Run the queue processor task every 40 milliseconds */ + mExecutorService.scheduleAtFixedRate( new QueueProcessor(), + 0, 40, TimeUnit.MILLISECONDS ); } } catch ( LineUnavailableException e ) @@ -109,10 +115,12 @@ public class MonoAudioOutput extends AudioOutput mixer.getMixerInfo().getName() + "]" ); } + mExecutorService = Executors.newSingleThreadScheduledExecutor( + new NamingThreadFactory( "audio (mono) output" ) ); + /* Run the queue processor task every 40 milliseconds */ - mProcessorTask = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_PROCESSING, new QueueProcessor(), - 40, TimeUnit.MILLISECONDS ); + mExecutorService.scheduleAtFixedRate( new QueueProcessor(), + 0, 40, TimeUnit.MILLISECONDS ); } } @@ -124,108 +132,102 @@ public class MonoAudioOutput extends AudioOutput public void dispose() { - if( mProcessorTask != null && mThreadPoolManager != null ) - { - mThreadPoolManager.cancel( mProcessorTask ); - } - - mProcessorTask = null; - super.dispose(); if( mOutput != null ) { mOutput.close(); } - - mOutput = null; - mGainControl = null; - mMuteControl = null; } public class QueueProcessor implements Runnable { private AtomicBoolean mProcessing = new AtomicBoolean(); + public QueueProcessor() + { + } + @Override public void run() { - try + /* The mProcessing flag ensures that only one instance of the + * processor can run at any given time */ + if( mProcessing.compareAndSet( false, true ) ) { - /* The mProcessing flag ensures that only one instance of the - * processor can run at any given time */ - if( mProcessing.compareAndSet( false, true ) ) + List packets = new ArrayList(); + + mBuffer.drainTo( packets ); + + if( packets.size() > 0 ) { - List packets = new ArrayList(); + broadcast( mAudioContinuationEvent ); - mBuffer.drainTo( packets ); - - if( packets.size() > 0 ) + for( AudioPacket packet: packets ) { - for( AudioPacket packet: packets ) + if( mCanProcessAudio ) { - broadcast( mAudioContinuationEvent ); - - if( mCanProcessAudio ) + if( isSquelched() ) { - if( packet.getType() == Type.AUDIO ) + checkStop(); + } + else if( packet.getType() == Type.AUDIO ) + { + float[] samples = packet.getAudioBuffer().getSamples(); + + /* Little-endian byte buffer */ + ByteBuffer buffer = + ByteBuffer.allocate( samples.length * 2 ) + .order( ByteOrder.LITTLE_ENDIAN ); + + ShortBuffer shortBuffer = buffer.asShortBuffer(); + + for( float sample: samples ) { - float[] samples = packet.getAudioBuffer().getSamples(); - - /* Little-endian byte buffer */ - ByteBuffer buffer = - ByteBuffer.allocate( samples.length * 2 ) - .order( ByteOrder.LITTLE_ENDIAN ); - - ShortBuffer shortBuffer = buffer.asShortBuffer(); - - for( float sample: samples ) - { - shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); - } - - //This is a blocking write - mOutput.write( buffer.array(), 0, buffer.array().length ); - - checkStart(); - - broadcast( packet.getAudioMetadata() ); - - mLastActivity = System.currentTimeMillis(); + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); } + + mOutput.write( buffer.array(), 0, + buffer.array().length ); + + checkStart(); + + broadcast( packet.getAudioMetadata() ); + + mLastActivity = System.currentTimeMillis(); } } } - else - { - checkStop(); - } - - mProcessing.set( false ); } - } - catch( Exception e ) - { - mLog.error( "Error while processing audio buffers", e ); + else + { + checkStop(); + } + + mProcessing.set( false ); } } private void checkStart() { - if( mCanProcessAudio && + if( mCanProcessAudio && !isSquelched() && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); + + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - mOutput.available() >= STOP_THRESHOLD ) + ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) { mOutput.drain(); mOutput.stop(); + + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } @@ -264,19 +266,4 @@ public class MonoAudioOutput extends AudioOutput { return mGainControl != null; } - - @Override - public void update( LineEvent event ) - { - LineEvent.Type type = event.getType(); - - if( type == LineEvent.Type.START ) - { - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); - } - else if( type == LineEvent.Type.STOP ) - { - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); - } - } } diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java index 1c3d1683..81892ca5 100644 --- a/src/audio/output/StereoAudioOutput.java +++ b/src/audio/output/StereoAudioOutput.java @@ -5,14 +5,13 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; @@ -25,8 +24,7 @@ import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; import audio.AudioPacket.Type; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; +import controller.NamingThreadFactory; /** * Mono Audio output in stereo format with automatic flow control based on the @@ -41,9 +39,9 @@ public class StereoAudioOutput extends AudioOutput private final static int BUFFER_SIZE = 96000; /* The queue processor will run every 20 milliseconds checking for inbound - * audio and automatically start or stop audio playback. We set the + * audio and automatically starting or stopping audio playback. We set the * data line available space threshold to start when we're 20 ms away from - * being full and the stop threshold when 20 ms away from being empty */ + * being full, and the stop threshold when 20 ms away from being empty */ private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25; private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS; private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS; @@ -51,16 +49,16 @@ public class StereoAudioOutput extends AudioOutput private SourceDataLine mOutput; private FloatControl mGainControl; private BooleanControl mMuteControl; + private AudioEvent mAudioStartEvent; private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; - private MixerChannel mMixerChannel; - private ScheduledFuture mProcessorTask; - public StereoAudioOutput( ThreadPoolManager threadPoolManager, - Mixer mixer, MixerChannel channel ) + private MixerChannel mMixerChannel; + + public StereoAudioOutput( Mixer mixer, MixerChannel channel ) { - super( threadPoolManager ); + super(); assert( channel == MixerChannel.LEFT || channel == MixerChannel.RIGHT ); @@ -81,7 +79,6 @@ public class StereoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE ); - mOutput.addLineListener( this ); mCanProcessAudio = true; } } @@ -116,10 +113,12 @@ public class StereoAudioOutput extends AudioOutput mixer.getMixerInfo().getName() + " | " + channel.name() + "]" ); } + mExecutorService = Executors.newSingleThreadScheduledExecutor( + new NamingThreadFactory( "audio (stereo) output" ) ); + /* Run the queue processor task every 40 milliseconds */ - mProcessorTask = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_PROCESSING, new QueueProcessor(), - 40, TimeUnit.MILLISECONDS ); + mExecutorService.scheduleAtFixedRate( new QueueProcessor(), + 0, 40, TimeUnit.MILLISECONDS ); } } @@ -131,30 +130,20 @@ public class StereoAudioOutput extends AudioOutput public void dispose() { - if( mProcessorTask != null && mThreadPoolManager != null ) - { - mThreadPoolManager.cancel( mProcessorTask ); - } - - mProcessorTask = null; - super.dispose(); if( mOutput != null ) { mOutput.close(); } - - mOutput = null; - mGainControl = null; - mMuteControl = null; } private ByteBuffer convert( float[] samples ) { /* Little-endian byte buffer */ - ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 ) - .order( ByteOrder.LITTLE_ENDIAN ); + ByteBuffer buffer = + ByteBuffer.allocate( samples.length * 4 ) + .order( ByteOrder.LITTLE_ENDIAN ); ShortBuffer shortBuffer = buffer.asShortBuffer(); @@ -189,72 +178,72 @@ public class StereoAudioOutput extends AudioOutput @Override public void run() { - try + /* The mProcessing flag ensures that only one instance of the + * processor can run at any given time */ + if( mProcessing.compareAndSet( false, true ) ) { - /* The mProcessing flag ensures that only one instance of the - * processor can run at any given time */ - if( mProcessing.compareAndSet( false, true ) ) + List packets = new ArrayList(); + + mBuffer.drainTo( packets ); + + if( packets.size() > 0 ) { - List packets = new ArrayList(); + broadcast( mAudioContinuationEvent ); - mBuffer.drainTo( packets ); - - if( packets.size() > 0 ) + for( AudioPacket packet: packets ) { - - for( AudioPacket packet: packets ) + if( mCanProcessAudio ) { - broadcast( mAudioContinuationEvent ); - - if( mCanProcessAudio ) + if( isSquelched() ) { - if( packet.getType() == Type.AUDIO ) - { - ByteBuffer buffer = convert( - packet.getAudioBuffer().getSamples() ); - - mOutput.write( buffer.array(), 0, - buffer.array().length ); - - checkStart(); - - broadcast( packet.getAudioMetadata() ); - - mLastActivity = System.currentTimeMillis(); - } + checkStop(); + } + else if( packet.getType() == Type.AUDIO ) + { + ByteBuffer buffer = convert( + packet.getAudioBuffer().getSamples() ); + + mOutput.write( buffer.array(), 0, + buffer.array().length ); + + checkStart(); + + broadcast( packet.getAudioMetadata() ); + + mLastActivity = System.currentTimeMillis(); } } } - else - { - checkStop(); - } - - mProcessing.set( false ); } - } - catch( Exception e ) - { - mLog.error( "Error while processing audio buffers", e ); + else + { + checkStop(); + } + + mProcessing.set( false ); } } private void checkStart() { - if( mCanProcessAudio && + if( mCanProcessAudio && !isSquelched() && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); + + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - mOutput.available() >= STOP_THRESHOLD ) + ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) { mOutput.drain(); mOutput.stop(); + + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } @@ -293,20 +282,4 @@ public class StereoAudioOutput extends AudioOutput { return mGainControl != null; } - - - @Override - public void update( LineEvent event ) - { - LineEvent.Type type = event.getType(); - - if( type == LineEvent.Type.START ) - { - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); - } - else if( type == LineEvent.Type.STOP ) - { - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); - } - } } diff --git a/src/bits/MessageFramer.java b/src/bits/MessageFramer.java index cd41737c..99d07888 100644 --- a/src/bits/MessageFramer.java +++ b/src/bits/MessageFramer.java @@ -58,16 +58,6 @@ public class MessageFramer implements Listener, mMessageLength = messageLength; } - public void reset() - { - for( MessageAssembler assembler: mMessageAssemblers ) - { - assembler.dispose(); - } - - mMessageAssemblers.clear(); - } - public void dispose() { mBroadcaster.dispose(); diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index 0ca9a00b..c5527970 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -36,20 +36,24 @@ public class ThreadPoolManager private HashMap,ThreadType> mTasks = new HashMap,ThreadType>(); - private ScheduledExecutorService mExecutor = - Executors.newScheduledThreadPool( THREAD_POOL_SIZE, - new NamingThreadFactory( "sdrtrunk" ) ); + private ScheduledExecutorService mExecutor; public ThreadPoolManager() { } - + public ScheduledFuture scheduleFixedRate( ThreadType type, Runnable command, long period, TimeUnit unit ) throws RejectedExecutionException { + if( mExecutor == null ) + { + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); + } + ScheduledFuture task = mExecutor.scheduleAtFixedRate( command, 0, period, unit ); @@ -61,16 +65,20 @@ public class ThreadPoolManager public void scheduleOnce( Runnable command, long delay, TimeUnit unit ) throws RejectedExecutionException { + if( mExecutor == null ) + { + mExecutor = Executors.newScheduledThreadPool( THREAD_POOL_SIZE, + new NamingThreadFactory( "sdrtrunk" ) ); + } + mExecutor.schedule( command, delay, unit ); } - public boolean cancel( ScheduledFuture task ) + public void cancel( ScheduledFuture task ) { - boolean success = task.cancel( true ); + task.cancel( true ); mTasks.remove( task ); - - return success; } public int getTaskCount( ThreadType type ) diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index a81ebe6e..db22429c 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -140,7 +140,7 @@ public class SDRTrunk AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); channelProcessingManager.addMessageListener( aliasActionManager ); - AudioManager audioManager = new AudioManager( threadPoolManager, sourceManager.getMixerManager() ); + AudioManager audioManager = new AudioManager( threadPoolManager ); channelProcessingManager.addAudioPacketListener( audioManager ); MapService mapService = new MapService( mSettingsManager ); @@ -471,7 +471,7 @@ public class SDRTrunk if( version != null ) { - sb.append( " v" ); + sb.append( " V" ); sb.append( version ); } } diff --git a/src/message/Message.java b/src/message/Message.java index 9b6b7a17..ac1ab775 100644 --- a/src/message/Message.java +++ b/src/message/Message.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014 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 @@ -17,94 +17,52 @@ ******************************************************************************/ package message; + import java.util.Collections; +import java.util.Date; import java.util.List; import map.Plottable; +import module.decode.event.MessageDetailsProvider; import alias.Alias; -public abstract class Message +public abstract class Message implements MessageDetailsProvider { protected long mTimeReceived; + protected MessageType mType; public Message() { - mTimeReceived = System.currentTimeMillis(); + this( MessageType.UN_KNWN ); } + public Message( MessageType type ) + { + mTimeReceived = System.currentTimeMillis(); + mType = type; + } + + public abstract Plottable getPlottable(); + + public abstract String toString(); + + public abstract boolean isValid(); + public long getTimeReceived() { return mTimeReceived; } - - /** - * Map plottable - */ - public Plottable getPlottable() + + public Date getDateReceived() { -//TODO: move this to an IPlottable interface that only gets implemented as needed - return null; + return new Date( mTimeReceived ); + } + + public MessageType getType() + { + return mType; } - /** - * Decoded textual representation of the message - */ - public abstract String toString(); - - /** - * Indicates if the message is valid and has passed crc/integrity checks - */ - public abstract boolean isValid(); - - /** - * Status of the CRC check of the message - */ - public abstract String getErrorStatus(); - - /** - * Parsed Message - * @return - */ - public abstract String getMessage(); - - /** - * Raw ( 0 & 1 ) message bits - */ - public abstract String getBinaryMessage(); - - - /** - * Decoded protocol - */ - public abstract String getProtocol(); - - /** - * Event - call, data, idle, etc. - */ - public abstract String getEventType(); - - /** - * Formatted from identifier - */ - public abstract String getFromID(); - - /** - * From identifier alias (from AliasManager) - */ - public abstract Alias getFromIDAlias(); - - /** - * Formatted to identifier - */ - public abstract String getToID(); - - /** - * To identifier alias (from AliasManager) - * @return - */ - public abstract Alias getToIDAlias(); - - /** * Provides a listing of aliases contained in the message. */ diff --git a/src/module/decode/event/CallEvent.java b/src/module/decode/event/CallEvent.java index 28990ad9..000cb27c 100644 --- a/src/module/decode/event/CallEvent.java +++ b/src/module/decode/event/CallEvent.java @@ -310,11 +310,6 @@ public abstract class CallEvent implements Comparable @Override public int compareTo( CallEvent other ) { - if( other != null && other == this ) - { - return 0; - } - return Long.compare( mEventStart, other.getEventStartTime() ); } } diff --git a/src/module/decode/event/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java index 390c4c59..65850196 100644 --- a/src/module/decode/event/MessageActivityModel.java +++ b/src/module/decode/event/MessageActivityModel.java @@ -19,7 +19,6 @@ package module.decode.event; import java.awt.EventQueue; import java.text.SimpleDateFormat; -import java.util.Date; import java.util.LinkedList; import javax.swing.table.AbstractTableModel; @@ -175,7 +174,7 @@ public class MessageActivityModel extends AbstractTableModel switch( columnIndex ) { case sTIME: - return mSDFTime.format( new Date( message.getTimeReceived() ) ); + return mSDFTime.format( message.getDateReceived() ); case sPROTOCOL: return message.getProtocol(); case sERROR_STATUS: diff --git a/src/module/decode/event/MessageDetailsProvider.java b/src/module/decode/event/MessageDetailsProvider.java new file mode 100644 index 00000000..bfce825e --- /dev/null +++ b/src/module/decode/event/MessageDetailsProvider.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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 + ******************************************************************************/ +package module.decode.event; + +import alias.Alias; + +/** + * Getter methods to support the Message Activity Table/Model + */ +public interface MessageDetailsProvider +{ + /** + * Indicates if the message is valid and has passed crc/integrity checks + */ + public abstract boolean isValid(); + + /** + * Status of the CRC check of the message + */ + public abstract String getErrorStatus(); + + /** + * Parsed Message + * @return + */ + public abstract String getMessage(); + + /** + * Raw ( 0 & 1 ) message bits + */ + public abstract String getBinaryMessage(); + + + /** + * Decoded protocol + */ + public abstract String getProtocol(); + + /** + * Event - call, data, idle, etc. + */ + public abstract String getEventType(); + + /** + * Formatted from identifier + */ + public abstract String getFromID(); + + /** + * From identifier alias (from AliasManager) + */ + public abstract Alias getFromIDAlias(); + + /** + * Formatted to identifier + */ + public abstract String getToID(); + + /** + * To identifier alias (from AliasManager) + * @return + */ + public abstract Alias getToIDAlias(); + +} diff --git a/src/module/decode/ltrnet/LTRCallEvent.java b/src/module/decode/ltrnet/LTRCallEvent.java index 3bb553ca..21a46afb 100644 --- a/src/module/decode/ltrnet/LTRCallEvent.java +++ b/src/module/decode/ltrnet/LTRCallEvent.java @@ -51,16 +51,17 @@ public class LTRCallEvent extends CallEvent */ public boolean isMatchingTalkgroup( LTRNetMessage message ) { - return isMatchingTalkgroup( message.getTalkgroupID() ); + String talkgroup = message.getTalkgroupID(); + + if( talkgroup != null && getToID() != null && + talkgroup.contentEquals( getToID() ) ) + { + return true; + } + + return false; } - public boolean isMatchingTalkgroup( String talkgroup ) - { - return talkgroup != null && - getToID() != null && - talkgroup.contentEquals( getToID() ); - } - private Alias getAlias( String talkgroup ) { if( hasAliasList() ) diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java index f5c1d3ca..ff12edca 100644 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -117,7 +117,6 @@ public class LTRNetDecoder extends Decoder @Override public void reset() { - mLTRMessageFramer.reset(); } @Override diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java index 228d68f5..365bd74c 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -80,7 +80,6 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer @Override public void reset() { - mLTRMessageFramer.reset(); } @Override diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java index 6dacaf17..94cd32b9 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014,2015 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 @@ -17,31 +17,21 @@ ******************************************************************************/ package module.decode.ltrstandard; -import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.Iterator; import java.util.TreeSet; import message.Message; import module.decode.DecoderType; import module.decode.event.CallEvent.CallEventType; import module.decode.ltrnet.LTRCallEvent; -import module.decode.ltrstandard.message.CallEndMessage; -import module.decode.ltrstandard.message.CallMessage; -import module.decode.ltrstandard.message.IdleMessage; -import module.decode.ltrstandard.message.LTRStandardMessage; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; import module.decode.state.DecoderStateEvent.Event; import module.decode.state.State; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import alias.Alias; import alias.AliasList; import audio.metadata.Metadata; @@ -49,17 +39,15 @@ import audio.metadata.MetadataType; public class LTRStandardDecoderState extends DecoderState { - private final static Logger mLog = - LoggerFactory.getLogger( LTRStandardDecoderState.class ); - - private Map mActiveCalls = new HashMap<>(); - private Set mTalkgroupsFirstHeard = new HashSet<>(); - private Set mTalkgroups = new TreeSet<>(); + private HashMap mActiveCalls = new HashMap(); + private HashSet mTalkgroupsFirstHeard = new HashSet(); + private TreeSet mTalkgroups = new TreeSet(); + private TreeSet mActiveLCNs = new TreeSet(); private String mTalkgroup; private Alias mTalkgroupAlias; + private int mChannelNumber; private long mFrequency; - private LCNTracker mLCNTracker = new LCNTracker(); public LTRStandardDecoderState( AliasList aliasList ) { @@ -82,128 +70,171 @@ public class LTRStandardDecoderState extends DecoderState { } + private LTRCallEvent getCurrentCallEvent() + { + return (LTRCallEvent)mCurrentCallEvent; + } + @Override public void receive( Message message ) { - if( message.isValid() && message instanceof LTRStandardMessage ) + if( message.isValid() ) { - switch( ((LTRStandardMessage)message).getMessageType() ) + if( message instanceof LTRStandardOSWMessage ) { - case CA_STRT: - CallMessage start = (CallMessage)message; - - int channel = start.getChannel(); - - mLCNTracker.processCallChannel( channel ); - - mLCNTracker.processFreeChannel( start.getFree() ); + LTRStandardOSWMessage ltr = ((LTRStandardOSWMessage)message); + + switch( ltr.getMessageType() ) + { + case CA_STRT: + if( 0 < ltr.getChannel() && ltr.getChannel() <=20 ) + { + /* If we haven't captured the LCN yet ... */ + if( mChannelNumber == 0 ) + { + mChannelNumber = ltr.getChannel(); + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } - //Only process calls on this LCN, or call detects for - //talkgroups that are homed on this LCN - if( mLCNTracker.isValidChannel( channel ) && - ( mLCNTracker.isCurrentChannel( channel ) || - mLCNTracker.isCurrentChannel( start.getHomeRepeater() ) ) ) - { - LTRCallEvent event = mActiveCalls.get( channel ); - - if( event == null || - !event.isMatchingTalkgroup( start.getToID() ) ) - { - //Check for different talkgroup - if( event != null ) - { - event.end(); - mActiveCalls.remove( channel ); - } + /* If this call event is for this channel, then update + * talkgroup and alias */ + if( ltr.getChannel() == mChannelNumber ) + { + mTalkgroup = ltr.getTalkgroupID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); + + mTalkgroupAlias = ltr.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + + LTRCallEvent current = getCurrentCallEvent(); - boolean current = mLCNTracker.isCurrentChannel( channel ); - - event = new LTRCallEvent.Builder( - DecoderType.LTR_STANDARD, - current ? CallEventType.CALL : CallEventType.CALL_DETECT ) - .to( start.getToID() ) - .aliasList( getAliasList() ) - .channel( start.getChannelFormatted() ) - .frequency( current ? mFrequency : 0 ) - .build(); - - mActiveCalls.put( channel, event ); - - broadcast( event ); - - if( mLCNTracker.isCurrentChannel( channel ) ) - { - mTalkgroup = start.getToID(); - broadcast( ChangedAttribute.TO_TALKGROUP ); - - processTalkgroup( mTalkgroup ); - - mTalkgroupAlias = start.getToIDAlias(); - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + /* Detect when we haven't reset from the previous + * call and now have a new call coming in with + * a different talkgroup */ + if( current != null && isDifferentTalkgroup( mTalkgroup ) ) + { + mCurrentCallEvent.end(); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; + } + + if( current == null ) + { + mCurrentCallEvent = + new LTRCallEvent.Builder( + DecoderType.LTR_STANDARD, CallEventType.CALL ) + .to( ltr.getTalkgroupID() ) + .aliasList( getAliasList() ) + .channel( String.valueOf( ltr.getChannel() ) ) + .frequency( mFrequency ) + .build(); - broadcast( new Metadata( MetadataType.TO, - mTalkgroup, mTalkgroupAlias, true ) ); - } - } - - broadcast( new DecoderStateEvent( this, - Event.CONTINUATION, State.CALL ) ); - } - break; - case CA_ENDD: - CallEndMessage end = (CallEndMessage)message; + broadcast( mCurrentCallEvent ); + + broadcast( new Metadata( MetadataType.TO, + mTalkgroup, mTalkgroupAlias, true ) ); - //Home channel is 31 for call end -- use the free channel - //as the call end channel - int repeater = end.getFree(); - - mLCNTracker.processCallChannel( repeater ); - - if( mLCNTracker.isCurrentChannel( repeater ) ) - { - mTalkgroup = end.getToID(); - broadcast( ChangedAttribute.TO_TALKGROUP ); - - processTalkgroup( mTalkgroup ); - - mTalkgroupAlias = end.getToIDAlias(); - broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); + broadcast( new DecoderStateEvent( this, + Event.START, State.CALL ) ); + } + else + { + broadcast( new DecoderStateEvent( this, + Event.CONTINUATION, State.CALL ) ); + } + } + } + break; + case CA_ENDD: + mTalkgroup = ltr.getTalkgroupID(); + broadcast( ChangedAttribute.TO_TALKGROUP ); - LTRCallEvent event = mActiveCalls.remove( repeater ); - - if( event != null ) - { - event.end(); - broadcast( event ); - broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); - } - } - break; - case SY_IDLE: - IdleMessage idle = (IdleMessage)message; + mTalkgroupAlias = ltr.getTalkgroupIDAlias(); + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); - int lcn = idle.getChannel(); + if( mCurrentCallEvent != null ) + { + mCurrentCallEvent.end(); + broadcast( mCurrentCallEvent ); + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + mCurrentCallEvent = null; + } + + int home = ltr.getHomeRepeater(); + + if( 0 < home && home <= 20 ) + { + String talkgroup = ltr.getTalkgroupID( false ); + + if( mTalkgroupsFirstHeard.contains( talkgroup ) ) + { + mTalkgroups.add( talkgroup ); + } + else + { + mTalkgroupsFirstHeard.add( talkgroup ); + } + } - mLCNTracker.processCallChannel( lcn ); - - break; - case UN_KNWN: - default: - break; + int channel = ltr.getChannel(); + + if( 0 < channel && channel <= 20 ) + { + if( channel == home ) + { + if( mChannelNumber != channel ) + { + mChannelNumber = channel; + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + mActiveLCNs.add( channel ); + } + + int free = ltr.getFree(); + + if( 0 < free && free <= 20 ) + { + mActiveLCNs.add( free ); + } + } + + break; + case SY_IDLE: + int idleChannel = ltr.getChannel(); + + if( idleChannel == ltr.getHomeRepeater() && + idleChannel == ltr.getFree() ) + { + if( mChannelNumber != idleChannel ) + { + mChannelNumber = idleChannel; + broadcast( ChangedAttribute.CHANNEL_NUMBER ); + } + + mActiveLCNs.add( idleChannel ); + } + broadcast( new DecoderStateEvent( this, + Event.CONTINUATION, State.IDLE ) ); + break; + case UN_KNWN: + default: + break; + } } } } - - private void processTalkgroup( String talkgroup ) + + /** + * Indicates if the talkgroup is different than the talkgroup specified in + * the current call event + */ + private boolean isDifferentTalkgroup( String talkgroup ) { - if( mTalkgroupsFirstHeard.contains( talkgroup ) ) - { - mTalkgroups.add( talkgroup ); - } - else - { - mTalkgroupsFirstHeard.add( talkgroup ); - } + return talkgroup != null && + mCurrentCallEvent != null && + mCurrentCallEvent.getToID() != null && + !mCurrentCallEvent.getToID().contentEquals( talkgroup ); } /** @@ -214,7 +245,8 @@ public class LTRStandardDecoderState extends DecoderState mActiveCalls.clear(); mTalkgroupsFirstHeard.clear(); mTalkgroups.clear(); - mLCNTracker.reset(); + mActiveLCNs.clear(); + mChannelNumber = 0; resetState(); } @@ -224,20 +256,13 @@ public class LTRStandardDecoderState extends DecoderState */ private void resetState() { - for( Integer key: mActiveCalls.keySet() ) + if( mCurrentCallEvent != null ) { - LTRCallEvent event = mActiveCalls.get( key ); - - if( event != null ) - { - event.end(); - - broadcast( event ); - } + mCurrentCallEvent.setEnd( System.currentTimeMillis() ); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; } - - mActiveCalls.clear(); - + mTalkgroup = null; broadcast( ChangedAttribute.TO_TALKGROUP ); @@ -257,12 +282,12 @@ public class LTRStandardDecoderState extends DecoderState public boolean hasChannelNumber() { - return mLCNTracker.getCurrentChannel() != 0; + return mChannelNumber != 0; } public int getChannelNumber() { - return mLCNTracker.getCurrentChannel(); + return mChannelNumber; } @Override @@ -297,28 +322,28 @@ public class LTRStandardDecoderState extends DecoderState sb.append( "Monitored LCN: " ); - if( hasChannelNumber() ) + if( mChannelNumber > 0 ) { - sb.append( getChannelNumber() ); + sb.append( mChannelNumber ); } else { - sb.append( "*Insufficient Data*" ); + sb.append( "unknown" ); } sb.append( "\n" ); - sb.append( "Active LCNs:\t" ); - - List lcns = mLCNTracker.getActiveLCNs(); + Integer[] lcns = mActiveLCNs.toArray( new Integer[0] ); - if( lcns.size() > 0 ) + sb.append( "Active LCNs:\t" ); + + if( lcns.length > 0 ) { - sb.append( mLCNTracker.getActiveLCNs() ); + sb.append( Arrays.toString( lcns ) ); } else { - sb.append( "*Insufficient Data*" ); + sb.append( "none" ); } sb.append( "\n\n" ); @@ -330,15 +355,19 @@ public class LTRStandardDecoderState extends DecoderState } else { - for( String talkgroup: mTalkgroups ) + Iterator it = mTalkgroups.iterator(); + + while( it.hasNext() ) { + String tgid = it.next(); + sb.append( " " ); - sb.append( talkgroup ); + sb.append( formatTalkgroup( tgid ) ); sb.append( " " ); if( hasAliasList() ) { - Alias alias = getAliasList().getTalkgroupAlias( talkgroup ); + Alias alias = getAliasList().getTalkgroupAlias( tgid ); if( alias != null ) { @@ -347,147 +376,30 @@ public class LTRStandardDecoderState extends DecoderState } sb.append( "\n" ); - } } return sb.toString(); } - /** - * Tracks the set of call and free channels for a system in order to - * dynamically determine the current LCN and minimize false triggers from - * bogus decoded LTR messages. Tracks the number of occurances of each - * LCN and uses a dynamic threshold to determine LCN validity. - */ - public class LCNTracker + public static String formatTalkgroup( String talkgroup ) { - private static final int DEFAULT_COUNT = 10; - private int[] mCallLCNCounts; - private int[] mFreeLCNCounts; - private int mCallHighestCount; - private int mFreeHighestCount; - private int mCurrentLCN; + StringBuilder sb = new StringBuilder(); - public LCNTracker() + if( talkgroup.length() == 6 ) { - reset(); - } - - public void logStatistics() - { - for( int x = 1; x <= 20; x++ ) - { - mLog.debug( "Call " + x + ": " + mCallLCNCounts[ x ] ); - } - mLog.debug( "Call Highest Count: " + mCallHighestCount ); - for( int x = 1; x <= 20; x++ ) - { - mLog.debug( "Free " + x + ": " + mFreeLCNCounts[ x ] ); - } - mLog.debug( "Free Highest Count: " + mFreeHighestCount ); + sb.append( talkgroup.substring( 0, 1 ) ); + sb.append( "-" ); + sb.append( talkgroup.substring( 1, 3 ) ); + sb.append( "-" ); + sb.append( talkgroup.substring( 3, 6 ) ); - mLog.debug( "Current LCN: " + mCurrentLCN ); + return sb.toString(); } - - public void reset() + else { - //Dim channel arrays to 21 -- ignore the 0 index - mCallLCNCounts = new int[ 21 ]; - mFreeLCNCounts = new int[ 21 ]; - mCallHighestCount = 3; - mFreeHighestCount = DEFAULT_COUNT; - mCurrentLCN = 0; - } - - public boolean isCurrentChannel( int channel ) - { - if( mCurrentLCN == 0 ) - { - return true; - } - else - { - return channel == mCurrentLCN; - } - } - - public int getCurrentChannel() - { - return mCurrentLCN; - } - - /** - * Indicates if the channel is valid based on the observed current and - * free channels reported by the system. Tracks the count of each - * reported free channel the highest free channel count is used as a - * threshold where a channel is valid when its count exceeds 20% of the - * highest free channel count. A channel is also valid when it is - * the currently monitored channel. - */ - public boolean isValidChannel( int channel ) - { - if( isCurrentChannel( channel ) ) - { - return true; - } - - int count = mFreeLCNCounts[ channel ]; - - int threshold = (int)( (double)mFreeHighestCount * 0.2 ); - - return count >= threshold; - } - - public void processCallChannel( int channel ) - { - if( 1 <= channel && channel <= 20 ) - { - mCallLCNCounts[ channel ]++; - - if( mCallLCNCounts[ channel ] > mCallHighestCount ) - { - mCallHighestCount = mCallLCNCounts[ channel ]; - mCurrentLCN = channel; - - broadcast( ChangedAttribute.CHANNEL_NUMBER ); - } - } - } - - public void processFreeChannel( int channel ) - { - if( 1 <= channel && channel <= 20 ) - { - mFreeLCNCounts[ channel ]++; - - if( mFreeLCNCounts[ channel ] > mFreeHighestCount ) - { - mFreeHighestCount = mFreeLCNCounts[ channel ]; - } - } - } - - public List getActiveLCNs() - { - List active = new ArrayList<>(); - - if( mFreeHighestCount > DEFAULT_COUNT ) - { - for( int x = 1; x <= 20; x++ ) - { - if( mCurrentLCN != 0 && isCurrentChannel( x ) ) - { - active.add( x ); - } - else if( isValidChannel( x ) ) - { - active.add( x ); - } - } - } - - return active; + return talkgroup; } } + } diff --git a/src/module/decode/ltrstandard/LTRStandardISWMessage.java b/src/module/decode/ltrstandard/LTRStandardISWMessage.java new file mode 100644 index 00000000..8f16ac1f --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardISWMessage.java @@ -0,0 +1,238 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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 + ******************************************************************************/ +package module.decode.ltrstandard; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import map.Plottable; +import message.MessageDirection; +import message.MessageType; +import alias.Alias; +import alias.AliasList; +import bits.BinaryMessage; + +public class LTRStandardISWMessage extends LTRStandardMessage +{ + private static final int sCALL_END = 31; + private static final int sIDLE = 255; + + public LTRStandardISWMessage( BinaryMessage message, AliasList list ) + { + super( message, MessageDirection.OSW, list ); + mMessageType = getType(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append( mDatestampFormatter.format( + new Date( System.currentTimeMillis() ) ) ); + sb.append( " LTR OSW [" ); + sb.append( mCRC.getAbbreviation() ); + sb.append( "] " ); + sb.append( getMessage() ); + pad( sb, 100 ); + sb.append( mMessage.toString() ); + + return sb.toString(); + } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + switch( mMessageType ) + { + case SY_IDLE: + sb.append( "IDLE AREA:" ); + sb.append( getArea() ); + sb.append( " LCN:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " HOME:" ); + sb.append( format( getHomeRepeater(), 2 ) ); + sb.append( " GRP:" ); + sb.append( format( getGroup(), 3 ) ); + sb.append( " FREE:" ); + sb.append( format( getFree(), 2 ) ); + break; + case CA_STRT: + sb.append( "CALL LCN:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " TG [ " ); + sb.append( getTalkgroupID() ); + sb.append( "/" ); + sb.append( getTalkgroupIDAlias() ); + sb.append( " ] FREE:" ); + sb.append( format( getFree(), 2 ) ); + break; + case CA_ENDD: + sb.append( "END* AREA:" ); + sb.append( getArea() ); + sb.append( " LCN:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " TG [" ); + sb.append( getTalkgroupID() ); + sb.append( "/" ); + sb.append( getTalkgroupIDAlias() ); + sb.append( "] FREE:" ); + sb.append( format( getFree(), 2 ) ); + break; + default: + sb.append( "UNKNOWN A:" ); + sb.append( getArea() ); + sb.append( " C:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " H:" ); + sb.append( format( getHomeRepeater(), 2 ) ); + sb.append( " G:" ); + sb.append( format( getGroup(), 3 ) ); + sb.append( " F:" ); + sb.append( format( getFree(), 2 ) ); + } + + return sb.toString(); + } + + public String getTalkgroupID() + { + return getTalkgroupID( true ); + } + + public String getTalkgroupID( boolean format ) + { + StringBuilder sb = new StringBuilder(); + + sb.append( getArea() ); + + if( format ) + { + sb.append( "-" ); + } + + sb.append( format( getHomeRepeater(), 2 ) ); + + if( format ) + { + sb.append( "-" ); + } + + sb.append( format( getGroup(), 3 ) ); + + return sb.toString(); + } + + public Alias getTalkgroupIDAlias() + { + StringBuilder sb = new StringBuilder(); + sb.append( getArea() ); + sb.append( format( getHomeRepeater(), 2 ) ); + sb.append( format( getGroup(), 3 ) ); + + return mAliasList.getTalkgroupAlias( sb.toString() ); + } + + public MessageType getType() + { + MessageType retVal = MessageType.UN_KNWN; + + int group = getGroup(); + + if( group == sIDLE ) + { + retVal = MessageType.SY_IDLE; + } + else + { + int channel = getChannel(); + + if( channel == sCALL_END ) + { + retVal = MessageType.CA_ENDD; + } + else + { + retVal = MessageType.CA_STRT; + } + } + + return retVal; + } + + @Override + public String getFromID() + { + if( mMessageType == MessageType.CA_STRT || + mMessageType == MessageType.CA_ENDD ) + { + return getTalkgroupID(); + } + else + { + return null; + } + } + + @Override + public Alias getFromIDAlias() + { + if( mMessageType == MessageType.CA_STRT || + mMessageType == MessageType.CA_ENDD ) + { + return getTalkgroupIDAlias(); + } + else + { + return null; + } + } + + @Override + public Plottable getPlottable() + { + // TODO Auto-generated method stub + return null; + } + + /** + * Provides a listing of aliases contained in the message. + */ + public List getAliases() + { + List aliases = new ArrayList(); + + Alias from = getFromIDAlias(); + + if( from != null ) + { + aliases.add( from ); + } + + Alias to = getToIDAlias(); + + if( to != null ) + { + aliases.add( to ); + } + + return aliases; + } +} diff --git a/src/module/decode/ltrstandard/LTRStandardMessage.java b/src/module/decode/ltrstandard/LTRStandardMessage.java new file mode 100644 index 00000000..408c7bd9 --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardMessage.java @@ -0,0 +1,254 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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 + ******************************************************************************/ +package module.decode.ltrstandard; + +import java.text.DecimalFormat; +import java.text.SimpleDateFormat; + +import message.Message; +import message.MessageDirection; +import message.MessageType; +import module.decode.DecoderType; +import alias.Alias; +import alias.AliasList; +import bits.BinaryMessage; +import edac.CRC; +import edac.CRCLTR; + +public abstract class LTRStandardMessage extends Message +{ + protected static final String sUNKNOWN = "**UNKNOWN**"; + protected static final int sINT_NULL_VALUE = -1; + protected static final double sDOUBLE_NULL_VALUE = -1.0D; + protected SimpleDateFormat mDatestampFormatter = + new SimpleDateFormat( "yyyyMMdd HHmmss" ); + protected DecimalFormat mDecimalFormatter = new DecimalFormat("0.00000"); + + protected static final int[] sSYNC = { 8,7,6,5,4,3,2,1,0 }; + protected static final int[] sAREA = { 9 }; + protected static final int[] sCHANNEL = { 14,13,12,11,10 }; + protected static final int[] sHOME_REPEATER = { 19,18,17,16,15 }; + protected static final int[] sGROUP = { 27,26,25,24,23,22,21,20 }; + protected static final int[] sFREE = { 32,31,30,29,28 }; + protected static final int[] sCRC = { 39,38,37,36,35,34,33 }; + + protected BinaryMessage mMessage; + protected MessageType mMessageType; + protected CRC mCRC; + protected AliasList mAliasList; + + public LTRStandardMessage( BinaryMessage message, + MessageDirection direction, + AliasList list ) + { + mMessage = message; + mCRC = CRCLTR.check( message, direction ); + mAliasList = list; + } + + public MessageType getMessageType() + { + return mMessageType; + } + + public boolean isValid() + { + return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY; + } + + /** + * Appends spaces to the end of the stringbuilder to make it length long + */ + protected void pad( StringBuilder sb, int length ) + { + while( sb.length() < length ) + { + sb.append( " " ); + } + } + + @Override + public String getBinaryMessage() + { + return mMessage.toString(); + } + + /** + * Returns a value formatted in hex notation. + * + * @param value - integer value + * @return - hex digits returned + */ + protected static String getHex( int value, int digits ) + { + return String.format( "%0" + digits + "X", value ); + } + + protected int getInt( int[] bits ) + { + int retVal = 0; + + for( int x = 0; x < bits.length; x++ ) + { + if( mMessage.get( bits[ x ] ) ) + { + retVal += 1< { @@ -36,12 +29,6 @@ public class LTRStandardMessageProcessor implements Listener private AliasList mAliasList; private Listener mMessageListener; - /** - * Processes raw binary messages and converts them to the correct message class - * - * @param direction - inbound (ISW) or outbound (OSW) - * @param list - alias list - */ public LTRStandardMessageProcessor( MessageDirection direction, AliasList list ) { mDirection = direction; @@ -49,66 +36,27 @@ public class LTRStandardMessageProcessor implements Listener } @Override - public void receive( BinaryMessage binaryMessage ) + public void receive( BinaryMessage buffer ) { if( mMessageListener != null ) { - //Inbound Status Word (ISW) is a bit-flipped version of the Outbound - //(OSW), so flip the bits and process it as an OSW - if( mDirection == MessageDirection.ISW ) + LTRStandardMessage message; + + if( mDirection == MessageDirection.OSW ) { - binaryMessage.flip( 0, 40 ); + message = new LTRStandardOSWMessage( buffer, mAliasList ); + } + else + { + buffer.flip( 0, 40 ); + + message = new LTRStandardISWMessage( buffer, mAliasList ); } - CRC crc = CRCLTR.check( binaryMessage, mDirection ); - - if( crc != CRC.FAILED_CRC && - crc != CRC.FAILED_PARITY ) - { - LTRStandardMessage message; - - int channel = binaryMessage.getInt( LTRStandardMessage.CHANNEL ); - int home = binaryMessage.getInt( LTRStandardMessage.HOME_REPEATER ); - int free = binaryMessage.getInt( LTRStandardMessage.FREE ); - int group = binaryMessage.getInt( LTRStandardMessage.GROUP ); - - if( isValidChannel( channel ) && - isValidChannel( home ) && - isValidChannel( free ) ) - { - if( channel == free && group == 255 ) - { - message = new IdleMessage( binaryMessage, mDirection, mAliasList, crc ); - } - else - { - message = new CallMessage( binaryMessage, mDirection, mAliasList, crc ); - } - } - else if( channel == 31 && - isValidChannel( home ) && - isValidChannel( free ) ) - { - message = new CallEndMessage( binaryMessage, mDirection, mAliasList, crc ); - } - else - { - message = new UnknownMessage( binaryMessage, mDirection, mAliasList, crc ); - } - - mMessageListener.receive( message ); - } + mMessageListener.receive( message ); } } - /** - * Checks the channel (LCN) number to ensure it is in the range 1 -20 - */ - private boolean isValidChannel( int channel ) - { - return ( 1 <= channel && channel <= 20 ); - } - public void setMessageListener( Listener listener ) { mMessageListener = listener; diff --git a/src/module/decode/ltrstandard/LTRStandardOSWMessage.java b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java new file mode 100644 index 00000000..df3bde82 --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardOSWMessage.java @@ -0,0 +1,248 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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 + ******************************************************************************/ +package module.decode.ltrstandard; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import map.Plottable; +import message.MessageDirection; +import message.MessageType; +import alias.Alias; +import alias.AliasList; +import bits.BinaryMessage; + +public class LTRStandardOSWMessage extends LTRStandardMessage +{ + private static final int sCALL_END = 31; + private static final int sIDLE = 255; + + public LTRStandardOSWMessage( BinaryMessage message, AliasList list ) + { + super( message, MessageDirection.OSW, list ); + mMessageType = getType(); + } + + @Override + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append( mDatestampFormatter.format( + new Date( System.currentTimeMillis() ) ) ); + sb.append( " LTR OSW [" ); + sb.append( mCRC.getAbbreviation() ); + sb.append( "] " ); + sb.append( getMessage() ); + pad( sb, 100 ); + sb.append( mMessage.toString() ); + + return sb.toString(); + } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + switch( mMessageType ) + { + case SY_IDLE: + sb.append( "IDLE AREA:" ); + sb.append( getArea() ); + sb.append( " LCN:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " HOME:" ); + sb.append( format( getHomeRepeater(), 2 ) ); + sb.append( " GRP:" ); + sb.append( format( getGroup(), 3 ) ); + sb.append( " FREE:" ); + sb.append( format( getFree(), 2 ) ); + break; + case CA_STRT: + sb.append( "CALL LCN:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " TG [ " ); + sb.append( getTalkgroupID() ); + sb.append( "/" ); + Alias alias = getTalkgroupIDAlias(); + if( alias != null ) + { + sb.append( getTalkgroupIDAlias().getName() ); + } + else + { + sb.append( "UNKNOWN" ); + } + sb.append( " ] FREE:" ); + sb.append( format( getFree(), 2 ) ); + break; + case CA_ENDD: + sb.append( "END* AREA:" ); + sb.append( getArea() ); + sb.append( " LCN:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " TG [" ); + sb.append( getTalkgroupID() ); + sb.append( "/" ); + Alias endAlias = getTalkgroupIDAlias(); + if( endAlias != null ) + { + sb.append( getTalkgroupIDAlias().getName() ); + } + else + { + sb.append( "UNKNOWN" ); + } + sb.append( "] FREE:" ); + sb.append( format( getFree(), 2 ) ); + break; + default: + sb.append( "UNKNOWN A:" ); + sb.append( getArea() ); + sb.append( " C:" ); + sb.append( format( getChannel(), 2 ) ); + sb.append( " H:" ); + sb.append( format( getHomeRepeater(), 2 ) ); + sb.append( " G:" ); + sb.append( format( getGroup(), 3 ) ); + sb.append( " F:" ); + sb.append( format( getFree(), 2 ) ); + } + + return sb.toString(); + } + + public String getTalkgroupID() + { + return getTalkgroupID( true ); + } + + public String getTalkgroupID( boolean format ) + { + StringBuilder sb = new StringBuilder(); + + sb.append( getArea() ); + + if( format ) + { + sb.append( "-" ); + } + + sb.append( format( getHomeRepeater(), 2 ) ); + + if( format ) + { + sb.append( "-" ); + } + + sb.append( format( getGroup(), 3 ) ); + + return sb.toString(); + } + + public Alias getTalkgroupIDAlias() + { + return mAliasList.getTalkgroupAlias( getTalkgroupID() ); + } + + public MessageType getType() + { + MessageType retVal = MessageType.UN_KNWN; + + /* Group 255 when the channel equals the free is the IDLE message */ + if( getGroup() == sIDLE && getChannel() == getFree() ) + { + retVal = MessageType.SY_IDLE; + } + else + { + int channel = getChannel(); + + if( channel == sCALL_END ) + { + retVal = MessageType.CA_ENDD; + } + else + { + retVal = MessageType.CA_STRT; + } + } + + return retVal; + } + + @Override + public String getFromID() + { + if( mMessageType == MessageType.CA_STRT || + mMessageType == MessageType.CA_ENDD ) + { + return getTalkgroupID(); + } + else + { + return null; + } + } + + @Override + public Alias getFromIDAlias() + { + if( mMessageType == MessageType.CA_STRT || + mMessageType == MessageType.CA_ENDD ) + { + return getTalkgroupIDAlias(); + } + else + { + return null; + } + } + + @Override + public Plottable getPlottable() + { + // TODO Auto-generated method stub + return null; + } + + /** + * Provides a listing of aliases contained in the message. + */ + public List getAliases() + { + List aliases = new ArrayList(); + + Alias from = getFromIDAlias(); + + if( from != null ) + { + aliases.add( from ); + } + + Alias to = getToIDAlias(); + + if( to != null ) + { + aliases.add( to ); + } + + return aliases; + } +} diff --git a/src/module/decode/ltrstandard/message/CallEndMessage.java b/src/module/decode/ltrstandard/message/CallEndMessage.java deleted file mode 100644 index e5023139..00000000 --- a/src/module/decode/ltrstandard/message/CallEndMessage.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.Alias; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class CallEndMessage extends LTRStandardMessage -{ - public CallEndMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.CA_ENDD; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "END* AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " TG [" ); - sb.append( getToID() ); - sb.append( "/" ); - Alias endAlias = getToIDAlias(); - if( endAlias != null ) - { - sb.append( getToIDAlias().getName() ); - } - else - { - sb.append( "UNKNOWN" ); - } - sb.append( "] FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/ltrstandard/message/CallMessage.java b/src/module/decode/ltrstandard/message/CallMessage.java deleted file mode 100644 index f7d44ca6..00000000 --- a/src/module/decode/ltrstandard/message/CallMessage.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.Alias; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class CallMessage extends LTRStandardMessage -{ - public CallMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.CA_STRT; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "CALL LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " TG [ " ); - sb.append( getToID() ); - sb.append( "/" ); - - Alias alias = getToIDAlias(); - - if( alias != null ) - { - sb.append( getToIDAlias().getName() ); - } - else - { - sb.append( "UNKNOWN" ); - } - - sb.append( " ] FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/ltrstandard/message/IdleMessage.java b/src/module/decode/ltrstandard/message/IdleMessage.java deleted file mode 100644 index ba8e4d97..00000000 --- a/src/module/decode/ltrstandard/message/IdleMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class IdleMessage extends LTRStandardMessage -{ - public IdleMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.SY_IDLE; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "IDLE AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " HOME:" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( " GRP:" ); - sb.append( getGroupFormatted() ); - sb.append( " FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/ltrstandard/message/LTRStandardMessage.java b/src/module/decode/ltrstandard/message/LTRStandardMessage.java deleted file mode 100644 index 87f381b2..00000000 --- a/src/module/decode/ltrstandard/message/LTRStandardMessage.java +++ /dev/null @@ -1,213 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import message.Message; -import message.MessageDirection; -import message.MessageType; -import module.decode.DecoderType; - -import org.apache.commons.lang3.StringUtils; - -import alias.Alias; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public abstract class LTRStandardMessage extends Message -{ - protected SimpleDateFormat mDatestampFormatter = - new SimpleDateFormat( "yyyyMMdd HHmmss" ); - - public static final int[] SYNC = { 0,1,2,3,4,5,6,7,8 }; - public static final int[] AREA = { 9 }; - public static final int[] CHANNEL = { 10,11,12,13,14 }; - public static final int[] HOME_REPEATER = { 15,16,17,18,19 }; - public static final int[] GROUP = { 20,21,22,23,24,25,26,27 }; - public static final int[] FREE = { 28,29,30,31,32 }; - public static final int[] CHECKSUM = { 33,34,35,36,37,38,39 }; - - protected BinaryMessage mMessage; - protected MessageDirection mMessageDirection; - protected AliasList mAliasList; - protected CRC mCRC; - - public LTRStandardMessage( BinaryMessage message, - MessageDirection direction, - AliasList list, - CRC crc ) - { - mMessage = message; - mMessageDirection = direction; - mAliasList = list; - mCRC = crc; - } - - public abstract MessageType getMessageType(); - - public boolean isValid() - { - return mCRC != CRC.FAILED_CRC && mCRC != CRC.FAILED_PARITY; - } - - @Override - public String toString() - { - StringBuilder sb = new StringBuilder(); - sb.append( mDatestampFormatter.format( - new Date( System.currentTimeMillis() ) ) ); - sb.append( " LTR " ); - sb.append( mMessageDirection.name() ); - sb.append( " [" ); - sb.append( mCRC.getAbbreviation() ); - sb.append( "] " ); - sb.append( getMessage() ); - - return sb.toString(); - } - - @Override - public String getBinaryMessage() - { - return mMessage.toString(); - } - - public CRC getCRC() - { - return mCRC; - } - - public int getArea() - { - return mMessage.getInt( AREA ); - } - - public int getChannel() - { - return mMessage.getInt( CHANNEL ); - } - - public String getChannelFormatted() - { - return StringUtils.leftPad( String.valueOf( getChannel() ), 2, "0" ); - } - - public int getHomeRepeater() - { - return mMessage.getInt( HOME_REPEATER ); - } - - public String getHomeRepeaterFormatted() - { - return StringUtils.leftPad( String.valueOf( getHomeRepeater() ), 2, "0" ); - } - - public int getGroup() - { - return mMessage.getInt( GROUP ); - } - - public String getGroupFormatted() - { - return StringUtils.leftPad( String.valueOf( getGroup() ), 3, "0" ); - } - - public int getFree() - { - return mMessage.getInt( FREE ); - } - - public String getFreeFormatted() - { - return StringUtils.leftPad( String.valueOf( getFree() ), 2, "0" ); - } - - @Override - public String getFromID() - { - return null; - } - - @Override - public Alias getFromIDAlias() - { - return null; - } - - @Override - public String getToID() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getArea() ); - sb.append( "-" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( "-" ); - sb.append( getGroupFormatted() ); - - return sb.toString(); - } - - @Override - public Alias getToIDAlias() - { - return mAliasList.getTalkgroupAlias( getToID() ); - } - - public List getAliases() - { - List aliases = new ArrayList<>(); - - Alias talkgroupAlias = getToIDAlias(); - - if( talkgroupAlias != null ) - { - aliases.add( talkgroupAlias ); - } - - return aliases; - } - - public int getCRCChecksum() - { - return mMessage.getInt( CHECKSUM ); - } - - @Override - public String getProtocol() - { - return DecoderType.LTR_STANDARD.getDisplayString(); - } - - @Override - public String getEventType() - { - return getMessageType().getDisplayText(); - } - - @Override - public String getErrorStatus() - { - return mCRC.getDisplayText(); - } -} diff --git a/src/module/decode/ltrstandard/message/UnknownMessage.java b/src/module/decode/ltrstandard/message/UnknownMessage.java deleted file mode 100644 index b975c667..00000000 --- a/src/module/decode/ltrstandard/message/UnknownMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package module.decode.ltrstandard.message; - -import message.MessageDirection; -import message.MessageType; -import alias.AliasList; -import bits.BinaryMessage; -import edac.CRC; - -public class UnknownMessage extends LTRStandardMessage -{ - public UnknownMessage( BinaryMessage message, MessageDirection direction, - AliasList list, CRC crc ) - { - super( message, direction, list, crc ); - } - - @Override - public MessageType getMessageType() - { - return MessageType.UN_KNWN; - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "UNKNOWN AREA:" ); - sb.append( getArea() ); - sb.append( " LCN:" ); - sb.append( getChannelFormatted() ); - sb.append( " HOME:" ); - sb.append( getHomeRepeaterFormatted() ); - sb.append( " GRP:" ); - sb.append( getGroupFormatted() ); - sb.append( " FREE:" ); - sb.append( getFreeFormatted() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java index 799fd5a0..45bc3372 100644 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -301,8 +301,8 @@ public class MPT1327Decoder extends Decoder @Override public void reset() { - mControlMessageFramer.reset(); - mTrafficMessageFramer.reset(); + // TODO Auto-generated method stub + } @Override diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index cc22c847..ad40aa52 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -26,11 +26,9 @@ import java.util.TreeSet; import message.Message; import module.decode.DecoderType; -import module.decode.config.DecodeConfiguration; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; import module.decode.mpt1327.MPT1327Message.IdentType; -import module.decode.state.ChangeChannelTimeoutEvent; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; @@ -43,8 +41,6 @@ import org.slf4j.LoggerFactory; import alias.Alias; import alias.AliasList; -import audio.metadata.Metadata; -import audio.metadata.MetadataType; import controller.channel.Channel.ChannelType; import controller.channel.map.ChannelMap; @@ -64,18 +60,15 @@ public class MPT1327DecoderState extends DecoderState private ChannelMap mChannelMap; private long mFrequency = 0; - private long mCallTimeout; - public MPT1327DecoderState( AliasList aliasList, - ChannelMap channelMap, - ChannelType channelType, - long callTimeout ) + public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap, + ChannelType channelType ) { super( aliasList ); mChannelMap = channelMap; + mChannelType = channelType; - mCallTimeout = callTimeout; } @Override @@ -259,42 +252,8 @@ public class MPT1327DecoderState extends DecoderState /* Traffic Channel Events */ case CLEAR: - mChannelNumber = mpt.getChannel(); - - broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); - break; case MAINT: - if( mChannelType == ChannelType.STANDARD ) - { - /** - * When we receive a MAINT message and we're configured - * as a standard channel, we need to apply the call - * timeout specified by the user. Otherwise we'll - * be using the shorter default call timeout - */ - broadcast( new ChangeChannelTimeoutEvent( this, - mChannelType, mCallTimeout ) ); - - if( mCurrentCallEvent == null ) - { - mCurrentCallEvent = new MPT1327CallEvent.Builder( CallEventType.CALL ) - .aliasList( getAliasList() ) - .channel( String.valueOf( mChannelNumber ) ) - .details( "MONITORED TRAFFIC CHANNEL" ) - .frequency( mFrequency ) - .to( mpt.getToID() ) - .build(); - - broadcast( mCurrentCallEvent ); - } - - broadcast(new Metadata(MetadataType.TO, - mpt.getToID(), mpt.getToIDAlias(), true)); - - broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) ); - - setToTalkgroup( mpt.getToID() ); - } + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); break; default: break; @@ -317,25 +276,6 @@ public class MPT1327DecoderState extends DecoderState mToTalkgroup = null; broadcast( ChangedAttribute.TO_TALKGROUP ); - - /** - * If this is a standard channel, reset the fade timeout to the default - * timeout. Once processing is underway, if we get a MAINT message, - * this indicates we're processing a traffic channel as a standard - * channel, so we'll issue a different call timeout then. - */ - if( mChannelType == ChannelType.STANDARD ) - { - broadcast( new ChangeChannelTimeoutEvent( this, mChannelType, - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000 ) ); - - if( mCurrentCallEvent != null ) - { - mCurrentCallEvent.end(); - broadcast( mCurrentCallEvent ); - mCurrentCallEvent = null; - } - } } public String getSite() diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 91ab112c..0ffe62ce 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -42,8 +42,6 @@ import module.decode.p25.message.ldu.lc.TelephoneInterconnectVoiceChannelUser; import module.decode.p25.message.ldu.lc.UnitToUnitVoiceChannelUser; import module.decode.p25.message.pdu.PDUMessage; import module.decode.p25.message.pdu.confirmed.PDUConfirmedMessage; -import module.decode.p25.message.pdu.confirmed.PDUTypeUnknown; -import module.decode.p25.message.pdu.confirmed.PacketData; import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextAccept; import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextReject; import module.decode.p25.message.pdu.confirmed.SNDCPActivateTDSContextRequest; @@ -120,6 +118,7 @@ import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdat import module.decode.p25.reference.IPProtocol; import module.decode.p25.reference.LinkControlOpcode; import module.decode.p25.reference.Response; +import module.decode.p25.reference.Service; import module.decode.p25.reference.Vendor; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; @@ -407,12 +406,12 @@ public class P25DecoderState extends DecoderState { if( hdu.isValid() ) { - broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) ); + State state = hdu.isEncrypted() ? State.ENCRYPTED : State.CALL; + + broadcast( new DecoderStateEvent( this, Event.START, state ) ); String to = hdu.getToID(); - - updateTo( to ); - + if( mCurrentCallEvent == null ) { mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL ) @@ -426,6 +425,7 @@ public class P25DecoderState extends DecoderState broadcast( mCurrentCallEvent ); } + updateTo( to ); } } @@ -437,7 +437,7 @@ public class P25DecoderState extends DecoderState { if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION ) { - broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); if( mCurrentCallEvent != null ) { @@ -615,14 +615,12 @@ public class P25DecoderState extends DecoderState module.decode.p25.message.tdu.lc.GroupVoiceChannelUser gvcuser = (module.decode.p25.message.tdu.lc.GroupVoiceChannelUser)tdulc; - broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) ); + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, + ( gvcuser.isEncrypted() ? State.ENCRYPTED : State.CALL ) ) ); String from = gvcuser.getSourceAddress(); String to = gvcuser.getGroupAddress(); - updateFrom( from ); - updateTo( to ); - if( mCurrentCallEvent == null ) { mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.GROUP_CALL ) @@ -638,6 +636,9 @@ public class P25DecoderState extends DecoderState broadcast( mCurrentCallEvent ); } + + updateFrom( from ); + updateTo( to ); } break; case MESSAGE_UPDATE: @@ -844,8 +845,6 @@ public class P25DecoderState extends DecoderState String to = tivcu.getAddress(); - updateTo( to ); - if( mCurrentCallEvent == null ) { mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.TELEPHONE_INTERCONNECT ) @@ -859,6 +858,8 @@ public class P25DecoderState extends DecoderState broadcast( mCurrentCallEvent ); } + + updateTo( to ); } break; case UNIT_AUTHENTICATION_COMMAND: @@ -921,10 +922,7 @@ public class P25DecoderState extends DecoderState (module.decode.p25.message.tdu.lc.UnitToUnitVoiceChannelUser)tdulc; String from = uuvcu.getSourceAddress(); - updateFrom( from ); - String to = uuvcu.getTargetAddress(); - updateTo( to ); if( mCurrentCallEvent != null ) { @@ -938,6 +936,9 @@ public class P25DecoderState extends DecoderState .to( to ) .build(); } + + updateFrom( from ); + updateTo( to ); } break; default: @@ -949,14 +950,35 @@ public class P25DecoderState extends DecoderState * Log optional vendor format messages that we don't yet support so that we * can understand those messages and eventually add support. */ - private void logAlternateVendorMessage( P25Message message ) + private void logAlternateVendorMessage( P25Message p25 ) { -// if( message.isValid() ) +// if( p25 instanceof TDULinkControlMessage ) // { -// mLog.info( "PLEASE NOTIFY DEVELOPER - UNRECOGNIZED P25 VENDOR FORMAT -" -// + " DUID:" + message.getDUID() -// + " MSG:" + message.getBinaryMessage() -// + " CLASS:" + message.getClass() ); +// TDULinkControlMessage tdulc = (TDULinkControlMessage)p25; +// +// mLog.debug( "TDULC - VENDOR [" + tdulc.getVendor() + +// "] SPECIFIC FORMAT FOR " + tdulc.getVendorOpcode().getLabel() + +// " " + tdulc.getBinaryMessage() ); +// } +// else if( p25 instanceof LDU1Message ) +// { +// LDU1Message ldu1 = (LDU1Message)p25; +// +// mLog.debug( "LDU1 - VENDOR [" + ldu1.getVendor() + +// "] SPECIFIC FORMAT FOR " + ldu1.getVendorOpcode().getLabel() + +// " " + ldu1.getBinaryMessage() ); +// } +// else if( p25 instanceof PDUMessage ) +// { +// PDUMessage pdu = (PDUMessage)p25; +// +// mLog.debug( "PDU - VENDOR [" + pdu.getVendor() + +// "] SPECIFIC FORMAT FOR " + pdu.getVendorOpcode().getLabel() + +// " " + pdu.getBinaryMessage() ); +// } +// else +// { +// mLog.debug( "P25 - ALTERNATE VENDOR MESSAGE [" + p25.getClass() + "]" ); // } } @@ -967,11 +989,15 @@ public class P25DecoderState extends DecoderState */ private void processLDU( LDUMessage ldu ) { - broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CALL ) ); + State state = ( ldu.isValid() && ldu.isEncrypted() ) ? + State.ENCRYPTED : State.CALL; + + broadcast( new DecoderStateEvent( this, Event.CONTINUATION, state ) ); if( mCurrentCallEvent == null ) { - mCurrentCallEvent = new P25CallEvent.Builder( CallEventType.CALL ) + mCurrentCallEvent = new P25CallEvent.Builder( + ( state == State.CALL ) ? CallEventType.CALL : CallEventType.ENCRYPTED_CALL ) .aliasList( getAliasList() ) .channel( mCurrentChannel ) .frequency( mCurrentChannelFrequency ) @@ -1031,7 +1057,7 @@ public class P25DecoderState extends DecoderState } break; case CALL_TERMINATION_OR_CANCELLATION: - broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); mCurrentCallEvent = null; if( !( ldu instanceof CallTermination ) ) @@ -1089,43 +1115,42 @@ public class P25DecoderState extends DecoderState } break; case GROUP_VOICE_CHANNEL_UPDATE: - if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate ) + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate ) { module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate gvcu = - (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; + (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdate)ldu; String userA = gvcu.getGroupAddressA(); + + if( !mCallDetectTalkgroups.contains( userA ) ) + { + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelA() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyA() ) + .to( userA ) + .build() ); + + mCallDetectTalkgroups.add( userA ); + } + String userB = gvcu.getGroupAddressB(); - if( mChannelType == ChannelType.STANDARD ) + if( userB != null && + !userB.contentEquals( "0000" ) && + !mCallDetectTalkgroups.contains( userB ) ) { - if( !mCallDetectTalkgroups.contains( userA ) ) - { - broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( getAliasList() ) - .channel( gvcu.getChannelA() ) - .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcu.getDownlinkFrequencyA() ) - .to( userA ) - .build() ); - - mCallDetectTalkgroups.add( userA ); - } + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcu.getChannelB() ) + .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcu.getDownlinkFrequencyB() ) + .to( userB ) + .build() ); - if( userB != null && - !userB.contentEquals( "0000" ) && - !mCallDetectTalkgroups.contains( userB ) ) - { - broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( getAliasList() ) - .channel( gvcu.getChannelB() ) - .details( ( gvcu.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcu.getDownlinkFrequencyB() ) - .to( userB ) - .build() ); - - mCallDetectTalkgroups.add( userB ); - } + mCallDetectTalkgroups.add( userB ); } } else @@ -1134,27 +1159,25 @@ public class P25DecoderState extends DecoderState } break; case GROUP_VOICE_CHANNEL_UPDATE_EXPLICIT: - if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit ) + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit ) { module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit gvcue = (module.decode.p25.message.ldu.lc.GroupVoiceChannelUpdateExplicit)ldu; String group = gvcue.getGroupAddress(); - if( mChannelType == ChannelType.STANDARD ) + if( !mCallDetectTalkgroups.contains( group ) ) { - if( !mCallDetectTalkgroups.contains( group ) ) - { - broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) - .aliasList( getAliasList() ) - .channel( gvcue.getTransmitChannel() ) - .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) - .frequency( gvcue.getDownlinkFrequency() ) - .to( group ) - .build() ); - - mCallDetectTalkgroups.add( group ); - } + broadcast( new P25CallEvent.Builder( CallEventType.CALL_DETECT ) + .aliasList( getAliasList() ) + .channel( gvcue.getTransmitChannel() ) + .details( ( gvcue.isEncrypted() ? "ENCRYPTED" : "" ) ) + .frequency( gvcue.getDownlinkFrequency() ) + .to( group ) + .build() ); + + mCallDetectTalkgroups.add( group ); } } else @@ -1164,32 +1187,29 @@ public class P25DecoderState extends DecoderState break; case GROUP_VOICE_CHANNEL_USER: /* Indicates the current user of the current channel */ - if( ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser ) + if( mChannelType == ChannelType.STANDARD && + ldu instanceof module.decode.p25.message.ldu.lc.GroupVoiceChannelUser ) { module.decode.p25.message.ldu.lc.GroupVoiceChannelUser gvcuser = (module.decode.p25.message.ldu.lc.GroupVoiceChannelUser)ldu; - updateFrom( gvcuser.getSourceAddress() ); - - updateTo( gvcuser.getGroupAddress() ); - - if( mChannelType == ChannelType.STANDARD ) + if( mCurrentCallEvent.getCallEventType() != CallEventType.GROUP_CALL ) { - if( mCurrentCallEvent.getCallEventType() != CallEventType.GROUP_CALL ) - { - mCurrentCallEvent.setCallEventType( CallEventType.GROUP_CALL ); - broadcast( mCurrentCallEvent ); - } - - if( mCurrentCallEvent.getDetails() == null ) - { - mCurrentCallEvent.setDetails( - ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) + - ( gvcuser.isEmergency() ? "EMERGENCY " : "" ) ); - - broadcast( mCurrentCallEvent ); - } + mCurrentCallEvent.setCallEventType( CallEventType.GROUP_CALL ); + broadcast( mCurrentCallEvent ); } + + if( mCurrentCallEvent.getDetails() == null ) + { + mCurrentCallEvent.setDetails( + ( gvcuser.isEncrypted() ? "ENCRYPTED " : "" ) + + ( gvcuser.isEmergency() ? "EMERGENCY " : "" ) ); + + broadcast( mCurrentCallEvent ); + } + + updateFrom( gvcuser.getSourceAddress() ); + updateTo( gvcuser.getGroupAddress() ); } else { @@ -1385,8 +1405,6 @@ public class P25DecoderState extends DecoderState TelephoneInterconnectVoiceChannelUser tivcu = (TelephoneInterconnectVoiceChannelUser)ldu; - updateTo( tivcu.getAddress() ); - if( mCurrentCallEvent.getCallEventType() != CallEventType.TELEPHONE_INTERCONNECT ) { mCurrentCallEvent.setCallEventType( CallEventType.TELEPHONE_INTERCONNECT ); @@ -1401,6 +1419,8 @@ public class P25DecoderState extends DecoderState broadcast( mCurrentCallEvent ); } + + updateTo( tivcu.getAddress() ); } else { @@ -1466,10 +1486,6 @@ public class P25DecoderState extends DecoderState UnitToUnitVoiceChannelUser uuvcu = (UnitToUnitVoiceChannelUser)ldu; - updateFrom( uuvcu.getSourceAddress() ); - - updateTo( uuvcu.getTargetAddress() ); - if( mCurrentCallEvent.getCallEventType() != CallEventType.UNIT_TO_UNIT_CALL ) { mCurrentCallEvent.setCallEventType( CallEventType.UNIT_TO_UNIT_CALL ); @@ -1484,6 +1500,9 @@ public class P25DecoderState extends DecoderState broadcast( mCurrentCallEvent ); } + + updateFrom( uuvcu.getSourceAddress() ); + updateTo( uuvcu.getTargetAddress() ); } else { @@ -1502,7 +1521,7 @@ public class P25DecoderState extends DecoderState private void processTSBK( TSBKMessage tsbk ) { /* Trunking Signalling Block Messages - indicates Control Channel */ - broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.CONTROL ) ); + broadcast( new DecoderStateEvent( this, Event.START, State.CONTROL ) ); if( tsbk.getVendor() == Vendor.STANDARD ) { @@ -1631,11 +1650,7 @@ public class P25DecoderState extends DecoderState */ private void processPDU( PDUMessage pdu ) { - if( pdu instanceof PacketData || pdu instanceof PDUTypeUnknown ) - { - broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) ); - } - else if( pdu instanceof PDUConfirmedMessage ) + if( pdu instanceof PDUConfirmedMessage ) { broadcast( new DecoderStateEvent( this, Event.CONTINUATION, State.DATA ) ); @@ -1643,7 +1658,7 @@ public class P25DecoderState extends DecoderState switch( pduc.getPDUType() ) { - case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: + case SN_ACTIVATE_TDS_CONTEXT_ACCEPT: SNDCPActivateTDSContextAccept satca = (SNDCPActivateTDSContextAccept)pduc; @@ -1655,7 +1670,7 @@ public class P25DecoderState extends DecoderState .to( satca.getLogicalLinkID() ) .build() ); break; - case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: + case SN_ACTIVATE_TDS_CONTEXT_REJECT: SNDCPActivateTDSContextReject satcr = (SNDCPActivateTDSContextReject)pduc; @@ -1680,7 +1695,7 @@ public class P25DecoderState extends DecoderState .from( satcreq.getLogicalLinkID() ) .build() ); break; - case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: + case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT: SNDCPDeactivateTDSContext sdtca = (SNDCPDeactivateTDSContext)pduc; @@ -1692,7 +1707,7 @@ public class P25DecoderState extends DecoderState .from( sdtca.getLogicalLinkID() ) .build() ); break; - case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: + case SN_DEACTIVATE_TDS_CONTEXT_REQUEST: SNDCPDeactivateTDSContext sdtcreq = (SNDCPDeactivateTDSContext)pduc; @@ -1704,7 +1719,7 @@ public class P25DecoderState extends DecoderState .from( sdtcreq.getLogicalLinkID() ) .build() ); break; - case SNDCP_RF_CONFIRMED_DATA: + case SN_RF_CONFIRMED_DATA: SNDCPUserData sud = (SNDCPUserData)pduc; StringBuilder sbFrom = new StringBuilder(); @@ -1733,10 +1748,8 @@ public class P25DecoderState extends DecoderState .to( pduc.getLogicalLinkID() ) .build() ); break; - case SNDCP_RF_UNCONFIRMED_DATA: - break; default: -// mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); + mLog.debug( "PDUC - Unrecognized Message: " + pduc.toString() ); break; } } @@ -3087,9 +3100,16 @@ public class P25DecoderState extends DecoderState if( mToTalkgroup == null || !mToTalkgroup.contentEquals( to ) ) { mToTalkgroup = to; - broadcast( ChangedAttribute.TO_TALKGROUP ); + if( mCurrentCallEvent != null && + ( mCurrentCallEvent.getToID() == null || + !mCurrentCallEvent.getToID().contentEquals( to ) ) ) + { + mCurrentCallEvent.setToID( to ); + broadcast( mCurrentCallEvent ); + } + if( hasAliasList() ) { mToAlias = getAliasList().getTalkgroupAlias( mToTalkgroup ); @@ -3098,20 +3118,12 @@ public class P25DecoderState extends DecoderState { mToAlias = null; } - + broadcast( ChangedAttribute.TO_TALKGROUP_ALIAS ); - - /* Send audio metadata update */ - broadcast( new Metadata( MetadataType.TO, mToTalkgroup, mToAlias, true ) ); - - if( mCurrentCallEvent != null && - ( mCurrentCallEvent.getToID() == null || - !mCurrentCallEvent.getToID().contentEquals( to ) ) ) - { - mCurrentCallEvent.setToID( to ); - broadcast( mCurrentCallEvent ); - } } + + /* Send audio metadata update */ + broadcast( new Metadata( MetadataType.TO, mToTalkgroup, mToAlias, true ) ); } } @@ -3127,6 +3139,14 @@ public class P25DecoderState extends DecoderState mFromTalkgroup = from; broadcast( ChangedAttribute.FROM_TALKGROUP ); + if( mCurrentCallEvent != null && + mCurrentCallEvent.getFromID() == null || + !mCurrentCallEvent.getFromID().contentEquals( from ) ) + { + mCurrentCallEvent.setFromID( from ); + broadcast( mCurrentCallEvent ); + } + if( hasAliasList() ) { mFromAlias = getAliasList().getTalkgroupAlias( mFromTalkgroup ); @@ -3137,19 +3157,10 @@ public class P25DecoderState extends DecoderState } broadcast( ChangedAttribute.FROM_TALKGROUP_ALIAS ); - - /* Send audio metadata update */ - broadcast( new Metadata( MetadataType.FROM, mFromTalkgroup, mFromAlias, true ) ); - - if( mCurrentCallEvent != null && - mCurrentCallEvent.getFromID() == null || - !mCurrentCallEvent.getFromID().contentEquals( from ) ) - { - mCurrentCallEvent.setFromID( from ); - broadcast( mCurrentCallEvent ); - } } + /* Send audio metadata update */ + broadcast( new Metadata( MetadataType.FROM, mFromTalkgroup, mFromAlias, true ) ); } } diff --git a/src/module/decode/p25/message/P25Message.java b/src/module/decode/p25/message/P25Message.java index dc33108f..e8421211 100644 --- a/src/module/decode/p25/message/P25Message.java +++ b/src/module/decode/p25/message/P25Message.java @@ -87,8 +87,6 @@ public class P25Message extends Message sb.append( " " ); sb.append( getDUID().getLabel() ); - sb.append( " DATA UNIT ID:" + mMessage.getInt( DUID ) ); - return sb.toString(); } diff --git a/src/module/decode/p25/message/pdu/PDUMessageFactory.java b/src/module/decode/p25/message/pdu/PDUMessageFactory.java index 1e11796d..26b25b1c 100644 --- a/src/module/decode/p25/message/pdu/PDUMessageFactory.java +++ b/src/module/decode/p25/message/pdu/PDUMessageFactory.java @@ -1,6 +1,5 @@ package module.decode.p25.message.pdu; -import module.decode.p25.message.pdu.confirmed.PacketData; import module.decode.p25.message.pdu.osp.control.AdjacentStatusBroadcastExtended; import module.decode.p25.message.pdu.osp.control.CallAlertExtended; import module.decode.p25.message.pdu.osp.control.GroupAffiliationQueryExtended; @@ -122,8 +121,7 @@ public class PDUMessageFactory } return new PDUMessage( message, duid, aliasList ); - case PACKET_DATA: - return new PacketData( message, aliasList ); + case UNCONFIRMED_MULTI_BLOCK_TRUNKING_CONTROL: return new PDUMessage( message, duid, aliasList ); default: diff --git a/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java index e8c08bcb..b695dd79 100644 --- a/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java +++ b/src/module/decode/p25/message/pdu/confirmed/PDUConfirmedMessageFactory.java @@ -48,27 +48,19 @@ public class PDUConfirmedMessageFactory case UNENCRYPTED_USER_DATA: switch( message.getPDUType() ) { - case SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT: + case SN_ACTIVATE_TDS_CONTEXT_ACCEPT: return new SNDCPActivateTDSContextAccept( message ); - case SNDCP_ACTIVATE_TDS_CONTEXT_REJECT: + case SN_ACTIVATE_TDS_CONTEXT_REJECT: return new SNDCPActivateTDSContextReject( message ); case SN_ACTIVATE_TDS_CONTEXT_REQUEST: return new SNDCPActivateTDSContextRequest( message ); - case SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT: - case SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST: + case SN_DEACTIVATE_TDS_CONTEXT_ACCEPT: + case SN_DEACTIVATE_TDS_CONTEXT_REQUEST: return new SNDCPDeactivateTDSContext( message ); - case SNDCP_RF_CONFIRMED_DATA: + case SN_RF_CONFIRMED_DATA: return new SNDCPUserData( message ); - case SNDCP_RF_UNCONFIRMED_DATA: + case SN_RF_UNCONFIRMED_DATA: break; - case PDU_TYPE_6: - case PDU_TYPE_7: - case PDU_TYPE_8: - case PDU_TYPE_9: - case PDU_TYPE_10: - case PDU_TYPE_11: - return new PDUTypeUnknown( message ); - case UNKNOWN: default: break; } diff --git a/src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java b/src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java deleted file mode 100644 index 0f08ca73..00000000 --- a/src/module/decode/p25/message/pdu/confirmed/PDUTypeUnknown.java +++ /dev/null @@ -1,55 +0,0 @@ -package module.decode.p25.message.pdu.confirmed; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class PDUTypeUnknown extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( PDUTypeUnknown.class ); - - public PDUTypeUnknown( PDUConfirmedMessage message ) - { - super( message ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " LLID:" ); - sb.append( getLogicalLinkID() ); - sb.append( " PACKET DATA UNIT CONFIRMED - " ); - sb.append( getPDUType().getLabel() ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - sb.append( mMessage.toString() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/p25/message/pdu/confirmed/PacketData.java b/src/module/decode/p25/message/pdu/confirmed/PacketData.java deleted file mode 100644 index e1b0af6d..00000000 --- a/src/module/decode/p25/message/pdu/confirmed/PacketData.java +++ /dev/null @@ -1,66 +0,0 @@ -package module.decode.p25.message.pdu.confirmed; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import alias.AliasList; -import bits.BinaryMessage; - -public class PacketData extends PDUConfirmedMessage -{ - public final static Logger mLog = - LoggerFactory.getLogger( PacketData.class ); - - public static final int DATA_BLOCK_START = 176; - - public PacketData( BinaryMessage message, AliasList aliasList ) - { - super( message, aliasList ); - } - - @Override - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "NAC:" ); - sb.append( getNAC() ); - sb.append( " PDUC LLID:" ); - sb.append( getLogicalLinkID() ); - - - sb.append( " PACKET DATA" ); - sb.append( " CRC[" ); - sb.append( getErrorStatus() ); - sb.append( "]" ); - - if( !mMessage.get( FINAL_FRAGMENT_FLAG ) ) - { - sb.append( " RESENDING" ); - } - - sb.append( " PACKET #" ); - sb.append( getPacketSequenceNumber() ); - - if( isFinalFragment() && getFragmentSequenceNumber() == 0 ) - { - sb.append( ".C" ); - } - else - { - sb.append( "." ); - sb.append( getFragmentSequenceNumber() ); - - if( isFinalFragment() ) - { - sb.append( "C" ); - } - } - - sb.append( " " ); - - sb.append( mMessage.toString() ); - - return sb.toString(); - } -} diff --git a/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java index ec8ec0b5..859303be 100644 --- a/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java +++ b/src/module/decode/p25/message/pdu/confirmed/SNDCPDeactivateTDSContext.java @@ -49,7 +49,7 @@ public class SNDCPDeactivateTDSContext extends PDUConfirmedMessage public String getDeactivationType() { - if( getPDUType() == PDUType.SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST ) + if( getPDUType() == PDUType.SN_DEACTIVATE_TDS_CONTEXT_REQUEST ) { return mMessage.getInt( DEACTIVATION_TYPE ) == 0 ? "ALL NSAPIS" : "THIS NSAPI"; diff --git a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java index 3e8ab315..e06ac784 100644 --- a/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java +++ b/src/module/decode/p25/message/tsbk/TSBKMessageFactory.java @@ -1,7 +1,5 @@ package module.decode.p25.message.tsbk; -import alias.AliasList; -import bits.BinaryMessage; import module.decode.p25.message.tsbk.motorola.ControlChannelBaseStationIdentification; import module.decode.p25.message.tsbk.motorola.MotorolaOpcode; import module.decode.p25.message.tsbk.motorola.MotorolaTSBKMessage; @@ -21,7 +19,6 @@ import module.decode.p25.message.tsbk.osp.control.ExtendedFunctionCommand; import module.decode.p25.message.tsbk.osp.control.GroupAffiliationQuery; import module.decode.p25.message.tsbk.osp.control.GroupAffiliationResponse; import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateNonVUHF; -import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateTDMA; import module.decode.p25.message.tsbk.osp.control.IdentifierUpdateVUHF; import module.decode.p25.message.tsbk.osp.control.LocationRegistrationResponse; import module.decode.p25.message.tsbk.osp.control.MessageUpdate; @@ -59,6 +56,8 @@ import module.decode.p25.message.tsbk.osp.voice.UnitToUnitVoiceChannelGrantUpdat import module.decode.p25.reference.DataUnitID; import module.decode.p25.reference.Opcode; import module.decode.p25.reference.Vendor; +import alias.AliasList; +import bits.BinaryMessage; public class TSBKMessageFactory { @@ -120,8 +119,6 @@ public class TSBKMessageFactory case IDENTIFIER_UPDATE_NON_VUHF: return new IdentifierUpdateNonVUHF( message, duid, aliasList ); - case IDENTIFIER_UPDATE_TDMA: - return new IdentifierUpdateTDMA( message, duid, aliasList ); case IDENTIFIER_UPDATE_VHF_UHF_BANDS: return new IdentifierUpdateVUHF( message, duid, aliasList ); diff --git a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java b/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java deleted file mode 100644 index ca31b7c6..00000000 --- a/src/module/decode/p25/message/tsbk/osp/control/IdentifierUpdateTDMA.java +++ /dev/null @@ -1,159 +0,0 @@ -package module.decode.p25.message.tsbk.osp.control; - -import module.decode.p25.reference.DataUnitID; -import module.decode.p25.reference.Opcode; -import alias.AliasList; -import bits.BinaryMessage; - -public class IdentifierUpdateTDMA extends IdentifierUpdate -{ - public static final int[] CHANNEL_TYPE = { 84,85,86,87 }; - public static final int TRANSMIT_OFFSET_SIGN = 88; - public static final int[] TRANSMIT_OFFSET = { 89,90,91,92,93,94,95,96,97,98, - 99,100,101 }; - - public IdentifierUpdateTDMA( BinaryMessage message, - DataUnitID duid, - AliasList aliasList ) - { - super( message, duid, aliasList ); - } - - @Override - public String getEventType() - { - return Opcode.IDENTIFIER_UPDATE_TDMA.getDescription(); - } - - public String getMessage() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getMessageStub() ); - - sb.append( " IDEN:" + getIdentifier() ); - sb.append( " BASE:" + getBaseFrequency() ); - sb.append( " BW:" + getBandwidth() ); - sb.append( " SPACING:" + getChannelSpacing() ); - sb.append( " OFFSET:" + getTransmitOffset() ); - sb.append( " TYPE:" + getChannelType().toString() ); - - return sb.toString(); - } - - public ChannelType getChannelType() - { - return ChannelType.fromValue( mMessage.getInt( CHANNEL_TYPE) ); - } - - /** - * Channel bandwidth in hertz - */ - public int getBandwidth() - { - return getChannelType().getBandwidth(); - } - - /** - * Transmit offset in hertz - */ - @Override - public long getTransmitOffset() - { - long offset = mMessage.getLong( TRANSMIT_OFFSET ) * - getChannelSpacing(); - - if( mMessage.get( TRANSMIT_OFFSET_SIGN ) ) - { - return offset; - } - else - { - return -offset; - } - } - - public enum ChannelType - { - TYPE_0( AccessType.FDMA, 12500, 1, Vocoder.HALF_RATE ), - TYPE_1( AccessType.FDMA, 12500, 1, Vocoder.FULL_RATE ), - TYPE_2( AccessType.FDMA, 6250, 1, Vocoder.HALF_RATE ), - TYPE_3( AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE ), - TYPE_4( AccessType.TDMA, 25000, 4, Vocoder.HALF_RATE ), - TYPE_5( AccessType.TDMA, 12500, 2, Vocoder.HALF_RATE ), //HD8PSK simulcast - UNKNOWN( AccessType.UNKNOWN, 0, 0, Vocoder.HALF_RATE ); - - private AccessType mAccessType; - private int mBandwidth; - private int mSlotsPerCarrier; - private Vocoder mVocoder; - - private ChannelType( AccessType accessType, - int bandwidth, - int slots, - Vocoder vocoder ) - { - mAccessType = accessType; - mBandwidth = bandwidth; - mSlotsPerCarrier = slots; - mVocoder = vocoder; - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - if( this.equals( TYPE_5 ) ) - { - sb.append("H-D8PSK SIMULCAST " ); - } - - sb.append( mAccessType.name() ); - sb.append( " BW:" + mBandwidth ); - sb.append( " SLOT/CARRIER:" + mSlotsPerCarrier ); - sb.append( " VOCODER:" + mVocoder.name() ); - - return sb.toString(); - } - - public static ChannelType fromValue( int value ) - { - if( 0 <= value && value <= 5 ) - { - return ChannelType.values()[ value ]; - } - - return ChannelType.UNKNOWN; - } - - public AccessType getAccessType() - { - return mAccessType; - } - - public int getBandwidth() - { - return mBandwidth; - } - - public int getSlotsPerCarrier() - { - return mSlotsPerCarrier; - } - - public Vocoder getVocoder() - { - return mVocoder; - } - } - - public enum AccessType - { - FDMA,TDMA,UNKNOWN; - } - - public enum Vocoder - { - HALF_RATE, FULL_RATE; - } -} diff --git a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java index c59d8f12..50c09e73 100644 --- a/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java +++ b/src/module/decode/p25/message/tsbk/osp/control/SyncBroadcast.java @@ -1,10 +1,8 @@ package module.decode.p25.message.tsbk.osp.control; -import java.text.DateFormat; -import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; -import java.util.SimpleTimeZone; +import java.util.GregorianCalendar; import java.util.TimeZone; import module.decode.p25.message.tsbk.TSBKMessage; @@ -14,21 +12,20 @@ import alias.AliasList; import bits.BinaryMessage; /** - * Sync Broadcast - used to broadcast FDMA-TDMA timing synchronization - * information so that a subscriber unit that is time synchronized on an FDMA - * control channel is also time synchronized with all TDMA traffic channels at - * the site. + * Sync Broadcast - used to broadcast FDMA-TDMA synchronization timestamp so + * that a subscriber unit that is time synchronized on an FDMA control channel + * is also synchronized with a TDMA traffic channel. */ public class SyncBroadcast extends TSBKMessage { - public static final int[] RESERVED = { 80,81,82,83,84,85,86,87,88,89,90,91,92 }; - public static final int SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE = 93; - public static final int MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED = 94; - public static final int[] LEAP_SECOND_CORRECTION = { 95,96 }; - public static final int LOCAL_TIME_OFFSET_VALID = 97; + public static final int US = 92; + public static final int IST = 93; + public static final int MMU = 94; + public static final int[] MC = { 95,96 }; + public static final int VL = 97; public static final int LOCAL_TIME_OFFSET_SIGN = 98; - public static final int[] LOCAL_TIME_OFFSET_HOURS = { 99,100,101,102 }; - public static final int LOCAL_TIME_OFFSET_HALF_HOUR = 103; + public static final int[] LOCAL_TIME_OFFSET = { 99,100,101,102 }; + public static final int LOCAL_TIME_OFFSET_HALF = 103; public static final int[] YEAR = { 104,105,106,107,108,109,110 }; public static final int[] MONTH = { 111,112,113,114 }; @@ -39,10 +36,7 @@ public class SyncBroadcast extends TSBKMessage 139,140,141,142,143 }; public static final int[] TSBK_CRC = { 144,145,146,147,148,149,150,151,152, 153,154,155,156,157,158,159 }; - - private static final DateFormat TIME_FORMATTER = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS Z" ); - private static final TimeZone NO_TIME_ZONE = new SimpleTimeZone( 0, "NONE" ); - + public SyncBroadcast( BinaryMessage message, DataUnitID duid, AliasList aliasList ) @@ -62,41 +56,24 @@ public class SyncBroadcast extends TSBKMessage sb.append( getMessageStub() ); - sb.append( " SYSTEM TIME" ); - - if( isSystemTimeNotLockedToExternalReference() ) - { - sb.append( " UNLOCKED" ); - } - - sb.append( ":" ); - - TIME_FORMATTER.setTimeZone( getTimeZone() ); - sb.append( " " + TIME_FORMATTER.format( new Date( getSystemTime() ) ) ); - - sb.append( " LEAP-SECOND CORRECTION:" + getLeapSecondCorrection() + "mS" ); - - if( isMicroslotsLockedToMinuteRollover() ) - { - sb.append( " MICROSLOT-MINUTE ROLLOVER:LOCKED" ); - } - else - { - sb.append( " MICROSLOT-MINUTE ROLLOVER:UNLOCKED" ); - } + sb.append( " " + new Date( getTime() ).toString() ); + sb.append( " TIME ZONE OFFSET:" + getTimeZone().toUpperCase() ); + sb.append( " US:" + isUS() ); + sb.append( " IST:" + isIST() ); + sb.append( " MMU:" + isMMU() ); + sb.append( " VL:" + isVL() ); return sb.toString(); } /** - * System Time (UTC) in milliseconds since java epoch + * Returns the time sync in java time (ie milliseconds since epoch ) */ - public long getSystemTime() + public long getTime() { - Calendar cal = Calendar.getInstance( TimeZone.getTimeZone( "UTC" ) ); - + Calendar cal = new GregorianCalendar(); cal.clear(); - + cal.setTimeZone( TimeZone.getTimeZone( "GMT" ) ); cal.set( Calendar.YEAR, getYear() ); cal.set( Calendar.MONTH, getMonth() - 1 ); cal.set( Calendar.DAY_OF_MONTH, getDay() ); @@ -107,165 +84,102 @@ public class SyncBroadcast extends TSBKMessage return cal.getTimeInMillis(); } - /** - * System Time not locked to external time reference indicator - */ - public boolean isSystemTimeNotLockedToExternalReference() + public boolean isUS() { - return mMessage.get( SYSTEM_TIME_NOT_LOCKED_TO_EXTERNAL_REFERENCE ); + return mMessage.get( US ); } /** - * Micro-slot-to-Minute rollover boundary lock indicator. - * - * Note: we invert the message value so that true indicates that the micro - * slots and minute rollovers are locked, whereas the ICD uses false to - * indicate a lock. - * - * @return true if the microslots rollover at each minute interval is locked - * to the system time minute rollover. - * - * @return false if the microslots and minutes rollover are not locked. This - * indicates that the microslots minute rollover is free rolling. - * + * Daylight Savings Time */ - public boolean isMicroslotsLockedToMinuteRollover() + public boolean isIST() { - return !mMessage.get( MICRO_SLOTS_TO_MINUTE_ROLLOVER_UNLOCKED ); + return mMessage.get( IST ); } /** - * Leap second correction value that should be applied to the system time. - * - * The leap-second correction field indicates the number of 2.5 millisecond - * units that must be added to the system time to account for the insertion - * of leap seconds when the system time is represented as UTC universal time. - * - * @return leap-second correction value in milliseconds 0.0-10.0 + * No idea ... */ - public double getLeapSecondCorrection() + public boolean isMMU() { - return (double)mMessage.getInt( LEAP_SECOND_CORRECTION ) * 2.5d; - } - - - - /** - * Indicates the local time offset fields contain valid information. - */ - public boolean isValidLocalTimeOffset() - { - return !mMessage.get( LOCAL_TIME_OFFSET_VALID ); + return mMessage.get( MMU ); } /** - * Local time zone offset from UTC universal time (zulu) when the VALID - * LOCAL TIME OFFSET flag indicates a valid offset. Otherwise, this method - * returns a static +00:00 indicating no local time offset. + * Millenium ...? */ - public TimeZone getTimeZone() + public int getMC() { - if( isValidLocalTimeOffset() ) - { - int offset = 0; - - offset += mMessage.getInt( LOCAL_TIME_OFFSET_HOURS ) * 3600000; - offset += mMessage.get( LOCAL_TIME_OFFSET_HALF_HOUR ) ? 1800000 : 0; - offset = mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? -offset : offset; - - return new SimpleTimeZone( offset, "LOCAL" ); - } - else - { - return NO_TIME_ZONE; - } + return mMessage.getInt( MC ); } /** - * System time - year. - * - * @return year in range 2000-2127 + * No idea .... */ + public boolean isVL() + { + return mMessage.get( VL ); + } + + /** + * Calculates the time zone from the local time offset field. + */ + public String getTimeZone() + { + StringBuilder sb = new StringBuilder(); + + sb.append( mMessage.get( LOCAL_TIME_OFFSET_SIGN ) ? "-" : "+" ); + + int hours = mMessage.getInt( LOCAL_TIME_OFFSET ); + sb.append( ( hours < 10 ? "0" : "" ) + String.valueOf( hours ) ); + + sb.append( ":" ); + + sb.append( mMessage.get( LOCAL_TIME_OFFSET_HALF ) ? "30" : "00" ); + + if( mMessage.get( IST ) ) + { + sb.append( " DST" ); + } + + return sb.toString(); + } + public int getYear() { return 2000 + mMessage.getInt( YEAR ); } - /** - * System time - month - * - * @return month 1-12 where 1=January and 12=December - */ public int getMonth() { return mMessage.getInt( MONTH ); } - /** - * System time - day of month - * - * @return day of month in range 1-31 - */ public int getDay() { return mMessage.getInt( DAY ); } - /** - * System time - hours - * - * @return hours in range 0 - 23 - */ public int getHours() { return mMessage.getInt( HOURS ); } - /** - * System time - minutes - * - * @return minutes in range 0 - 59 - */ public int getMinutes() { return mMessage.getInt( MINUTES ); } - /** - * System time - milli-seconds - * - * Note: sub-millisecond values are rounded to nearest millisecond unit to - * conform to java Date internal milli-second precision level. - * - * @return milli-seconds in range 0 - 59999; - */ public int getMilliSeconds() { return (int)( (double)getMicroSlots() * 7.5 ); } /** - * TDMA Micro Slots. - * - * Number of 7.5 millisecond micro slots since the last minute rollover, or - * since the last micro-slot counter rollover when the the micro-slot to - * minute rollover is unlocked. - * - * @see isMicroslotsLockedToMinuteRollover() method. - * - * This value supports mobile subscriber timing alignment with a TDMA traffic - * channel when a channel is granted from the control channels, so that when - * the mobile changes from the control channel to the traffic channel, it is - * already aligned with the traffic channel TDMA timing. - * - * Super frames occur every 360 ms (48 micro-slots) on FDMA and Ultra frames - * occur every 4 super frames on TDMA. - * - * Valid micro-slot values range 0-7999 and represent 60,000 - * milliseconds ( 8000 x 7.5 ms). - * - * @return number of 7.5 mS micro-slots since last minute or micro-slot - * rollover + * Micro Slots - each uslot = 7.5 milliseconds. The value ranges 0 - 7999 + * and represents 60,000 milliseconds ( 8000 x 7.5 ms). Superframes occur + * every 360 ms (48 micro-slots) on FDMA and Ultraframes occur every 4 + * super frames on TDMA. */ public int getMicroSlots() { diff --git a/src/module/decode/p25/reference/Opcode.java b/src/module/decode/p25/reference/Opcode.java index d94afec8..4dbf8870 100644 --- a/src/module/decode/p25/reference/Opcode.java +++ b/src/module/decode/p25/reference/Opcode.java @@ -53,7 +53,7 @@ public enum Opcode TDMA_SYNC_BROADCAST( "TDMA_SYNC_BCST ", "FDMA-TDMA Sync Broadcast", 48 ), RESERVED_31( "RESERVED_31 ", "Reserved", 49 ), RESERVED_32( "RESERVED_32 ", "Reserved", 50 ), - IDENTIFIER_UPDATE_TDMA( "IDEN_UPDATE_TDMA", "Identifier Update for TDMA", 51 ), + RESERVED_33( "RESERVED_33 ", "Reserved", 51 ), IDENTIFIER_UPDATE_VHF_UHF_BANDS( "IDEN_UPDATE_VUHF", "Identifier Update for VHF/UHF Bands", 52 ), TIME_DATE_ANNOUNCEMENT( "TIME_DATE_ANNOUN", "Time and Date Announcement", 53 ), ROAMING_ADDRESS_COMMAND( "ROAM_ADDR_CMD ", "Roaming Address Command", 54 ), diff --git a/src/module/decode/p25/reference/PDUType.java b/src/module/decode/p25/reference/PDUType.java index 20acb4ab..23f5da1e 100644 --- a/src/module/decode/p25/reference/PDUType.java +++ b/src/module/decode/p25/reference/PDUType.java @@ -3,18 +3,12 @@ package module.decode.p25.reference; public enum PDUType { /* Outbound */ - SNDCP_ACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-ACTIVATE TDS CONTEXT ACCEPT", 0 ), - SNDCP_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SNDCP-DEACTIVATE TDS CONTEXT ACCEPT", 1 ), - SNDCP_DEACTIVATE_TDS_CONTEXT_REQUEST( "SNDCP-DEACTIVATE TDS CONTEXT REQUEST", 2 ), - SNDCP_ACTIVATE_TDS_CONTEXT_REJECT( "SNDCP-ACTIVATE TDS CONTEXT REJECT", 3 ), - SNDCP_RF_UNCONFIRMED_DATA( "SNDCP-RF UNCONFIRMED DATA", 4 ), - SNDCP_RF_CONFIRMED_DATA( "SNDCP-RF CONFIRMED DATA", 5 ), - PDU_TYPE_6( "PDU TYPE 6 UNKNOWN", 6 ), - PDU_TYPE_7( "PDU TYPE 7 UNKNOWN", 7 ), - PDU_TYPE_8( "PDU TYPE 8 UNKNOWN", 8 ), - PDU_TYPE_9( "PDU TYPE 9 UNKNOWN", 9 ), - PDU_TYPE_10( "PDU TYPE 10 UNKNOWN", 10 ), - PDU_TYPE_11( "PDU TYPE 11 UNKNOWN", 11 ), + SN_ACTIVATE_TDS_CONTEXT_ACCEPT( "SN-ACTIVATE TDS CONTEXT ACCEPT", 0 ), + SN_DEACTIVATE_TDS_CONTEXT_ACCEPT( "SN-DEACTIVATE TDS CONTEXT ACCEPT", 1 ), + SN_DEACTIVATE_TDS_CONTEXT_REQUEST( "SN-DEACTIVATE TDS CONTEXT REQUEST", 2 ), + SN_ACTIVATE_TDS_CONTEXT_REJECT( "SN-ACTIVATE TDS CONTEXT REJECT", 3 ), + SN_RF_UNCONFIRMED_DATA( "SN-RF UNCONFIRMED DATA", 4 ), + SN_RF_CONFIRMED_DATA( "SN-RF CONFIRMED DATA", 5 ), /* Inbound */ SN_ACTIVATE_TDS_CONTEXT_REQUEST( "SN-ACTIVATE TDS CONTEXT REQUEST", 0 ), @@ -42,7 +36,7 @@ public enum PDUType public static PDUType fromValue( int value, boolean outbound ) { - if( outbound && 0 <= value && value <= 11 ) + if( outbound && 0 <= value && value <= 5 ) { return values()[ value ]; } diff --git a/src/module/decode/p25/reference/ServiceAccessPoint.java b/src/module/decode/p25/reference/ServiceAccessPoint.java index 1040aa7c..ea1c735d 100644 --- a/src/module/decode/p25/reference/ServiceAccessPoint.java +++ b/src/module/decode/p25/reference/ServiceAccessPoint.java @@ -2,7 +2,7 @@ package module.decode.p25.reference; public enum ServiceAccessPoint { - UNENCRYPTED_USER_DATA( "UNENCRYPTED USER DATA", 0 ), + UNENCRYPTED_USER_DATA( "USER DATA", 0 ), ENCRYPTED_USER_DATA( "ENCRYPTED USER DATA", 1 ), CIRCUIT_DATA( "CIRCUIT DATA", 2 ), CIRCUIT_DATA_CONTROL( "CIRCUIT DATA CONTROL", 3 ), diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java index e125541d..46381d8d 100644 --- a/src/module/decode/passport/PassportDecoder.java +++ b/src/module/decode/passport/PassportDecoder.java @@ -106,7 +106,6 @@ public class PassportDecoder extends Decoder @Override public void reset() { - mPassportMessageFramer.reset(); } @Override diff --git a/src/module/decode/state/ChangeChannelTimeoutEvent.java b/src/module/decode/state/ChangeChannelTimeoutEvent.java deleted file mode 100644 index a2950171..00000000 --- a/src/module/decode/state/ChangeChannelTimeoutEvent.java +++ /dev/null @@ -1,29 +0,0 @@ -package module.decode.state; - -import controller.channel.Channel.ChannelType; - -/** - * Allows decoders to change the call timeout setting in the channel state - */ -public class ChangeChannelTimeoutEvent extends DecoderStateEvent -{ - private ChannelType mChannelType; - private long mCallTimeout; - - public ChangeChannelTimeoutEvent( Object source, ChannelType channelType, long timeout ) - { - super( source, Event.CHANGE_CALL_TIMEOUT, State.IDLE ); - mChannelType = channelType; - mCallTimeout = timeout; - } - - public ChannelType getChannelType() - { - return mChannelType; - } - - public long getCallTimeout() - { - return mCallTimeout; - } -} diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index 5cd8f9b8..be9f9a89 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -136,13 +136,10 @@ public class ChannelList extends JPanel implements ChannelEventListener public void setSelectedChannel( Channel channel ) { - synchronized( mDisplayedPanels ) + /* Send channel selection to each channel panel */ + for( ChannelCollectionPanel panel: mDisplayedPanels.values() ) { - /* Send channel selection to each channel panel */ - for( ChannelCollectionPanel panel: mDisplayedPanels.values() ) - { - panel.setSelectedChannel( channel ); - } + panel.setSelectedChannel( channel ); } } @@ -261,6 +258,20 @@ public class ChannelList extends JPanel implements ChannelEventListener idleMenu.add( new ColorSettingResetMenuItem( mSettingsManager, ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_IDLE ) ); + JMenu noTunerMenu = new JMenu( "No Tuner" ); + channelsMenu.add( noTunerMenu ); + + noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) ); + noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_MIDDLE_NO_TUNER ) ); + noTunerMenu.add( new JSeparator() ); + + noTunerMenu.add( new ColorSettingMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) ); + noTunerMenu.add( new ColorSettingResetMenuItem( mSettingsManager, + ColorSettingName.CHANNEL_STATE_GRADIENT_TOP_NO_TUNER ) ); + JMenu labelMenu = new JMenu( "Labels" ); colorMenu.add( labelMenu ); diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java index d75c904e..3b538349 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/module/decode/state/ChannelState.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014-2016 Dennis Sheirer + * Copyright (C) 2014,2015 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 @@ -17,14 +17,20 @@ ******************************************************************************/ package module.decode.state; -import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent.Event; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sample.Listener; import audio.metadata.IMetadataProvider; import audio.metadata.Metadata; import audio.metadata.MetadataReset; @@ -34,13 +40,38 @@ import audio.squelch.SquelchState; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import controller.channel.Channel.ChannelType; -import module.Module; -import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEvent; -import module.decode.event.ICallEventProvider; -import module.decode.state.DecoderStateEvent.Event; -import sample.Listener; +/** + * Channel state tracks the overall state of all processing modules and decoders + * configured for the channel and provides squelch control events. + * + * Primary channel state provides a state machine for tracking decode events + * during a voice or data call through it's life cycle of CALL/DATA, FADE, END, + * and then back to an IDLE state. This class also provides squelch control + * to support any audio processing. + * + * State Descriptions: + * IDLE No voice or data call activity + * CALL/DATA A voice or data call has started, or is continuing + * CONTROL Control channel + * FADE The phase after a voice or data call when either an explicit + * call end has been received, or when no new signalling updates + * have been received, and the fade timer has expired. This phase + * allows for gui updates to signal to the user that the call is + * ended, while continuing to display the call details for the user + * END The phase after the voice or data call FADE, when the call is + * now over, and the gui should be reset to IDLE. + * + * When a call is started, invoke mTimerService.startFadeTimer() to start the + * call fade monitoring timer process that will automatically signal a call end + * once no more call signalling has been detected after the fade timeout has + * occurred, by invoking the fade() method. + * + * When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke + * the mTimerService.startResetTimer(). Once the reset timeout occurs, + * the reset() method will be called, allowing the channel state to perform + * any reset actions, like clearing status and id labels on any view panels. + */ public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider, IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider @@ -52,6 +83,8 @@ public class ChannelState extends Module implements ICallEventProvider, public static final long RESET_TIMEOUT_DELAY = 2000; private State mState = State.IDLE; + private long mTrafficChannelTimeout = + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; private Listener mCallEventListener; private Listener mChangedAttributeListener; @@ -64,15 +97,12 @@ public class ChannelState extends Module implements ICallEventProvider, private boolean mSquelchLocked = false; private boolean mSelected = false; - private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY; - private long mTrafficChannelFadeTimeout = - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; private long mFadeTimeout; - private long mEndTimeout; + private long mResetTimeout; private StateMonitor mStateMonitor = new StateMonitor(); private AtomicBoolean mMonitoring = new AtomicBoolean(); - private ScheduledFuture mStateMonitorFuture; + private ScheduledFuture mRunningMonitor; private ThreadPoolManager mThreadPoolManager; private ChannelType mChannelType; @@ -80,97 +110,32 @@ public class ChannelState extends Module implements ICallEventProvider, private TrafficChannelStatusListener mTrafficChannelStatusListener; private CallEvent mTrafficChannelCallEvent; - /** - * Channel state tracks the overall state of all processing modules and decoders - * configured for the channel and provides squelch control and decoder state - * reset events. - * - * Uses a state enumeration that defines allowable channel state transitions in - * order to track a call or data decode event from start to finish. Uses a - * timer to monitor for inactivity and to provide a FADE period that indicates - * to the user that the activity has stopped while continuing to provide details - * about the call, before the state is reset to IDLE. - * - * Since this class is multi-threaded between decoder events and the internal - * timer that monitors for inactivity, this class uses the mState object for - * thread synchronization. - * - * State Descriptions: - * IDLE Normal state. No voice or data call activity - * CALL/DATA/ENCRYPTED/CONTROL - * Decoding states. - * FADE The phase after a voice or data call when either an explicit - * call end has been received, or when no new signalling updates - * have been received, and the fade timer has expired. This phase - * allows for gui updates to signal to the user that the call is - * ended, while continuing to display the call details for the user - * TEARDOWN Indicates a traffic channel that will be torn down for reuse. - */ public ChannelState( ThreadPoolManager manager, ChannelType channelType ) { mThreadPoolManager = manager; mChannelType = channelType; } - /** - * Resets this channel state and prepares it for reuse. - */ @Override public void reset() { - broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); - broadcast( new MetadataReset() ); - - mState = State.IDLE; - broadcast( ChangedAttribute.CHANNEL_STATE ); } @Override public void start() { - //Start a monitoring task to check for FADE and END event timeouts - if( mMonitoring.compareAndSet( false, true ) ) - { - if( mStateMonitorFuture == null && mStateMonitor != null ) - { - try - { - mStateMonitorFuture = mThreadPoolManager.scheduleFixedRate( - ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); - } - catch( RejectedExecutionException ree ) - { - mLog.error( "state monitor scheduled task rejected", ree ); - mMonitoring.set( false ); - } - } - } } @Override public void stop() { mSquelchLocked = false; - - //Shutdown the state montoring task - if( mMonitoring.compareAndSet( true, false ) ) - { - if( mStateMonitorFuture != null ) - { - boolean success = mThreadPoolManager.cancel( mStateMonitorFuture ); - - if( !success ) - { - mLog.error("Couldn't stop monitoring scheduled future" ); - } - } - - mStateMonitorFuture = null; - } } public void dispose() { + stopMonitor(); + mCallEventListener = null; mChangedAttributeListener = null; mDecoderStateListener = null; @@ -179,34 +144,9 @@ public class ChannelState extends Module implements ICallEventProvider, mStateMonitor = null; } - private boolean isStandardChannel() - { - return mChannelType == ChannelType.STANDARD; - } - - private boolean isTrafficChannel() - { - return mChannelType == ChannelType.TRAFFIC; - } - - public void setStandardChannelTimeout( long milliseconds ) - { - mStandardChannelFadeTimeout = milliseconds; - - if( mChannelType == ChannelType.STANDARD ) - { - mFadeTimeout = mStandardChannelFadeTimeout; - } - } - public void setTrafficChannelTimeout( long milliseconds ) { - mTrafficChannelFadeTimeout = milliseconds; - - if( mChannelType == ChannelType.TRAFFIC ) - { - mFadeTimeout = mTrafficChannelFadeTimeout; - } + mTrafficChannelTimeout = milliseconds; } public void setSelected( boolean selected ) @@ -224,18 +164,71 @@ public class ChannelState extends Module implements ICallEventProvider, return mState; } + private boolean isIdleOrEndingState() + { + return mState == State.FADE || + mState == State.END || + mState == State.IDLE; + } + + + /** + * Starts the state monitor running when we're not in IDLE state. Calls to + * start an already started monitor are ignored. + */ + private void startMonitor() + { + if( mMonitoring.compareAndSet( false, true ) ) + { + if( mRunningMonitor == null && mStateMonitor != null ) + { + mRunningMonitor = mThreadPoolManager.scheduleFixedRate( + ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); + } + else + { + throw new RuntimeException( "Channel state monitor's scheduled " + + "future pointer was not null" ); + } + } + } + + /** + * Stops the state monitor when we're in an IDLE state. Calls to stop an + * already stopped monitor are ignored. + */ + private void stopMonitor() + { + if( mMonitoring.compareAndSet( true, false ) ) + { + if( mRunningMonitor != null ) + { + mThreadPoolManager.cancel( mRunningMonitor ); + } + + mRunningMonitor = null; + } + } + /** * Updates the fade timeout threshold to the current time plus delay */ private void updateFadeTimeout() { - if( isTrafficChannel() ) + /* Standard channel uses the FADE_TIMEOUT_DELAY and traffic channels + * use the TRAFFIC_FADE_TIMEOUT_DELAY. Some traffic channels (e.g. MPT) + * don't have signaling on the traffic channel that can be used to + * update the fade timeout, so we set the timeout value larger to ensure + * the channel doesn't fade before the end of the call. */ + long current = System.currentTimeMillis(); + + long timeout = current + + ( mChannelType == ChannelType.STANDARD ? FADE_TIMEOUT_DELAY : + mTrafficChannelTimeout ); + + if( timeout > mFadeTimeout ) { - mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout; - } - else - { - mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout; + mFadeTimeout = timeout; } } @@ -244,13 +237,13 @@ public class ChannelState extends Module implements ICallEventProvider, */ private void updateResetTimeout() { - if( isTrafficChannel() ) + if( mChannelType == ChannelType.TRAFFIC ) { - mEndTimeout = System.currentTimeMillis(); + mResetTimeout = System.currentTimeMillis(); } else { - mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY; + mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY; } } @@ -292,120 +285,120 @@ public class ChannelState extends Module implements ICallEventProvider, */ protected void setState( State state ) { - synchronized( mState ) + if( mState == state ) { - if( state == mState ) + switch( state ) { - if( State.CALL_STATES.contains( state ) ) - { + case CALL: + case CONTROL: + case DATA: + case ENCRYPTED: updateFadeTimeout(); - } + break; + default: + break; } - else if( state != mState && mState.canChangeTo( state ) ) + } + else if( mState.canChangeTo( state ) ) + { + switch( state ) { - switch( state ) - { - case CONTROL: - //Don't allow traffic channels to be control channels, - //otherwise they can't transition to call tear down - if( isStandardChannel() ) - { - broadcast( SquelchState.SQUELCH ); - updateFadeTimeout(); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - } - break; - case DATA: - case ENCRYPTED: + case CONTROL: + //Don't allow traffic channels to be control channels, + //otherwise they won't get call tear down + if( mChannelType != ChannelType.TRAFFIC ) + { broadcast( SquelchState.SQUELCH ); updateFadeTimeout(); + startMonitor(); mState = state; broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - case CALL: - broadcast( SquelchState.UNSQUELCH ); - updateFadeTimeout(); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - case FADE: - processFadeState(); - break; - case IDLE: - processIdleState(); - break; - case TEARDOWN: - processTeardownState(); - break; - case RESET: - mState = State.IDLE; - break; - default: - break; + } + break; + case DATA: + case ENCRYPTED: + broadcast( SquelchState.SQUELCH ); + updateFadeTimeout(); + startMonitor(); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + case CALL: + broadcast( SquelchState.UNSQUELCH ); + updateFadeTimeout(); + startMonitor(); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + case FADE: + broadcast( SquelchState.SQUELCH ); + updateResetTimeout(); + startMonitor(); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + case END: + broadcast( SquelchState.SQUELCH ); + broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); + broadcast( new MetadataReset() ); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + case IDLE: + broadcast( SquelchState.SQUELCH ); + stopMonitor(); + + if( mChannelType == ChannelType.TRAFFIC ) + { + if( mTrafficChannelStatusListener != null ) + { + mTrafficChannelStatusListener.callEnd(); + mTrafficChannelStatusListener = null; + } + + if( mTrafficChannelCallEvent != null ) + { + mTrafficChannelCallEvent.end(); + + broadcast( mTrafficChannelCallEvent ); + mTrafficChannelCallEvent = null; + } + } + + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + default: + break; + } + } + } + + public class StateMonitor implements Runnable + { + @Override + public void run() + { + if( getState() == State.FADE ) + { + if( mResetTimeout <= System.currentTimeMillis() ) + { + setState( State.END ); } } + else if( getState() == State.END ) + { + setState( State.IDLE ); + } else { -// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" ); + if( mFadeTimeout <= System.currentTimeMillis() ) + { + setState( State.FADE ); + } } } } - - /** - * Sets the state and processes related actions - */ - private void processFadeState() - { - updateResetTimeout(); - mState = State.FADE; - - broadcast( SquelchState.SQUELCH ); - broadcast( ChangedAttribute.CHANNEL_STATE ); - } - - private void processIdleState() - { - broadcast( SquelchState.SQUELCH ); - - if( mState == State.FADE ) - { - broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); - broadcast( new MetadataReset() ); - } - - mState = State.IDLE; - - broadcast( ChangedAttribute.CHANNEL_STATE ); - } - - private void processTeardownState() - { - broadcast( SquelchState.SQUELCH ); - - mState = State.TEARDOWN; - - broadcast( ChangedAttribute.CHANNEL_STATE ); - - if( mTrafficChannelCallEvent != null ) - { - mTrafficChannelCallEvent.end(); - - broadcast( mTrafficChannelCallEvent ); - mTrafficChannelCallEvent = null; - } - - if( mTrafficChannelStatusListener != null ) - { - mTrafficChannelStatusListener.callEnd(); - mTrafficChannelStatusListener = null; - } - else - { - mLog.error( "Traffic channel status listener was null - couldn't " - + "teardown the channel"); - } - } /** * Broadcasts the call event to the registered listener @@ -597,95 +590,35 @@ public class ChannelState extends Module implements ICallEventProvider, broadcast( SquelchState.UNSQUELCH ); mSquelchLocked = true; break; - case CHANGE_CALL_TIMEOUT: - if( event instanceof ChangeChannelTimeoutEvent ) - { - ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event; - - if( timeout.getChannelType() == ChannelType.STANDARD ) - { - setStandardChannelTimeout( timeout.getCallTimeout() ); - } - else - { - setTrafficChannelTimeout( timeout.getCallTimeout() ); - } - } case CONTINUATION: + if( isIdleOrEndingState() ) + { + setState( event.getState() ); + } + else + { + updateFadeTimeout(); + } + break; case DECODE: - case START: - if( State.CALL_STATES.contains( event.getState() ) ) + if( getState() == State.IDLE ) { setState( event.getState() ); } break; case END: - if( isTrafficChannel() ) - { - setState( State.TEARDOWN ); - } - else - { - setState( State.FADE ); - } + setState( State.FADE ); break; case RESET: /* Channel State does not respond to reset events */ break; + case START: + setState( event.getState() ); + break; default: break; } } } } - - /** - * Monitors decoder state events to automatically transition the channel - * state to IDLE (standard channel) or to TEARDOWN (traffic channel) when - * decoding stops or the monitored channel returns to a no signal state. - * - * Provides a FADE transition state to allow for momentary decoding dropouts - * and to allow the user access to call details for a fade period upon - * call end. - */ - public class StateMonitor implements Runnable - { - @Override - public void run() - { - try - { - synchronized( mState ) - { - if( State.CALL_STATES.contains( mState ) && - mFadeTimeout <= System.currentTimeMillis() ) - { - mState = State.FADE; - processFadeState(); - } - else if( mState == State.FADE && - mEndTimeout <= System.currentTimeMillis() ) - { - if( isTrafficChannel() ) - { - processTeardownState(); - } - else - { - processIdleState(); - } - } - } - } - catch( Exception e ) - { - mLog.error( "Exception thrown while state monitor is running " + - "- state [" + getState() + - "] current [" + System.currentTimeMillis() + - "] mResetTimeout [" + mEndTimeout + - "] mFadeTimeout [" + mFadeTimeout + - "]", e ); - } - } - } } diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/module/decode/state/DecoderStateEvent.java index fa60b7d8..19623bbc 100644 --- a/src/module/decode/state/DecoderStateEvent.java +++ b/src/module/decode/state/DecoderStateEvent.java @@ -56,7 +56,6 @@ public class DecoderStateEvent public enum Event { ALWAYS_UNSQUELCH, - CHANGE_CALL_TIMEOUT, CONTINUATION, DECODE, END, diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java index 141c5ea6..2820429d 100644 --- a/src/module/decode/state/State.java +++ b/src/module/decode/state/State.java @@ -1,10 +1,5 @@ package module.decode.state; -import java.util.EnumSet; - -/** - * Details the set of states for a channel and the allowable transition states - */ public enum State { IDLE( "IDLE" ) @@ -12,8 +7,7 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state != TEARDOWN && - state != RESET; + return true; } }, CALL( "CALL" ) @@ -21,11 +15,11 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state == CONTROL || + return state == CALL || + state == CONTROL || state == DATA || state == ENCRYPTED || - state == FADE || - state == TEARDOWN; + state == FADE; } }, DATA( "DATA" ) @@ -35,9 +29,9 @@ public enum State { return state == CALL || state == CONTROL || - state == ENCRYPTED || - state == FADE || - state == TEARDOWN; + state == DATA || + state == State.ENCRYPTED || + state == FADE; } }, ENCRYPTED( "ENCRYPTED" ) @@ -45,7 +39,7 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state == FADE || state == TEARDOWN; + return state == FADE; } }, CONTROL( "CONTROL" ) @@ -53,8 +47,7 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state == IDLE || - state == FADE; + return true; } }, FADE( "FADE" ) @@ -62,36 +55,20 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state != FADE && - state != RESET; - } + return state != FADE; //All states except fade allowed + } }, - RESET( "RESET" ) + END( "END" ) { - @Override - public boolean canChangeTo(State state) - { - return state == IDLE; - } - }, - - TEARDOWN( "TEARDOWN" ) - { - @Override - public boolean canChangeTo(State state) - { - return state == RESET; - } - }; + @Override + public boolean canChangeTo( State state ) + { + return state != FADE; //Fade is only disallowed state + } + }; private String mDisplayValue; - public static final EnumSet CALL_STATES = - EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); - - public static final EnumSet IDLE_STATES = - EnumSet.of( IDLE, FADE ); - private State( String displayValue ) { mDisplayValue = displayValue; diff --git a/src/org/jdesktop/swingx/JXMapViewer.java b/src/org/jdesktop/swingx/JXMapViewer.java index e0bd81f9..81ffc083 100644 --- a/src/org/jdesktop/swingx/JXMapViewer.java +++ b/src/org/jdesktop/swingx/JXMapViewer.java @@ -22,9 +22,10 @@ import java.awt.image.BufferedImage; import java.beans.DesignMode; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.net.URL; import java.util.Set; -import javax.swing.ImageIcon; +import javax.imageio.ImageIO; import javax.swing.JPanel; import org.jdesktop.swingx.mapviewer.GeoPosition; @@ -125,13 +126,13 @@ public class JXMapViewer extends JPanel implements DesignMode // make a dummy loading image try { - ImageIcon imageIcon = new ImageIcon( "images/loading.png" ); - this.setLoadingImage( imageIcon.getImage() ); + URL url = JXMapViewer.class.getResource("mapviewer/resources/loading.png"); + this.setLoadingImage(ImageIO.read(url)); } catch (Throwable ex) { - mLog.error( "could not load 'loading.png'" ); + mLog.error( "JXMapViewer could not load 'loading.png'" ); BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB); Graphics2D g2 = img.createGraphics(); diff --git a/src/properties/SystemProperties.java b/src/properties/SystemProperties.java index a36a171b..bf4b33f4 100644 --- a/src/properties/SystemProperties.java +++ b/src/properties/SystemProperties.java @@ -82,13 +82,18 @@ public class SystemProperties { out = new FileOutputStream( propsPath.toString() ); - String comments = "SDRTrunk - SDR Trunking Decoder Application Settings"; + String comments = + "SDRTrunk - SDR Trunking Decoder Application Settings"; mProperties.store( out, comments ); + + mLog.info( "SystemProperties - saved current properties [" + + propsPath.toString() + "]" ); } catch( Exception e ) { - mLog.error( "exception while saving application properties", e ); + mLog.error( "SystemProperties - exception while saving " + + "application properties", e ); } finally { @@ -141,7 +146,8 @@ public class SystemProperties } catch ( IOException e ) { - mLog.error( "exception while creating app folder [" + folder + "]", e ); + mLog.error( "SystemProperties - exception while creating " + + "app folder [" + folder + "]", e ); } } @@ -152,11 +158,11 @@ public class SystemProperties { if( mPropertiesPath == null ) { - mLog.info( "no properties file loaded - using defaults" ); + mLog.info( "SystemProperties - no properties file loaded - using defaults" ); } else { - mLog.info( "application properties loaded [" + mPropertiesPath.toString() + "]" ); + mLog.info( "SystemProperties - application properties loaded [" + mPropertiesPath.toString() + "]" ); } } @@ -178,7 +184,7 @@ public class SystemProperties } catch ( FileNotFoundException e ) { - mLog.error( "exception while opening inputstream on " + + mLog.error( "SDRTrunk - exception while opening inputstream on " + "application properties file", e ); } @@ -190,7 +196,7 @@ public class SystemProperties } catch ( IOException e ) { - mLog.error( "exception while loading properties " + + mLog.error( "SDRTrunk - exception while loading properties " + "inputstream into SystemProperties", e ); } finally @@ -206,7 +212,8 @@ public class SystemProperties } } - mLog.info( "loaded [" + propertiesPath.toString() + "]" ); + mLog.info( "SystemProperties - loaded [" + + propertiesPath.toString() + "]" ); } /** diff --git a/src/settings/ColorSetting.java b/src/settings/ColorSetting.java index d0cb3e1f..6040fce4 100644 --- a/src/settings/ColorSetting.java +++ b/src/settings/ColorSetting.java @@ -123,7 +123,7 @@ public class ColorSetting extends Setting "Control Gradient Middle", "Channel Control State Gradient Middle" ), CHANNEL_STATE_GRADIENT_TOP_DATA( Color.BLACK, "Data Gradient Top", "Channel Data State Gradient Top" ), - CHANNEL_STATE_GRADIENT_MIDDLE_DATA( new Color( 0xCC00CC ), + CHANNEL_STATE_GRADIENT_MIDDLE_DATA( Color.BLUE, "Data Gradient Middle", "Channel Data State Gradient Middle" ), CHANNEL_STATE_GRADIENT_TOP_FADE( Color.BLACK, "Fade Gradient Top", "Channel Fade State Gradient Top" ), diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index df430aa6..ee25bd78 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -437,7 +437,8 @@ public class SettingsManager } /* Something happened ... the above should always return an icon */ - mLog.error( "couldn't return an icon named [" + name + "] of heigh [" + height + "]" ); + mLog.error( "SettingsManager - couldn't return an icon named [" + + name + "] of heigh [" + height + "]" ); } return null; @@ -635,7 +636,8 @@ public class SettingsManager return r820TConfig; default: - throw new IllegalArgumentException( "tuner type is unrecognized [" + + throw new IllegalArgumentException( "TunerConfiguration" + + "Directory - tuner type is unrecognized [" + type.toString() + "]" ); } } @@ -709,7 +711,8 @@ public class SettingsManager } catch( Exception e ) { - mLog.error( "couldn't create file to save settings [" + filePath.toString() + "]", e ); + mLog.error( "SettingsManager - couldn't create file to save " + + "settings [" + filePath.toString() + "]", e ); } OutputStream out = null; @@ -730,12 +733,14 @@ public class SettingsManager } catch ( JAXBException e ) { - mLog.error( "jaxb exception while saving settings", e ); + mLog.error( "SettingsManager - jaxb exception while saving " + + "settings", e ); } } catch ( Exception e ) { - mLog.error( "couldn't open outputstream to save settings [" + filePath.toString() + "]" ); + mLog.error( "SettingsManager - coulcn't open outputstream to " + + "save settings [" + filePath.toString() + "]" ); } finally { @@ -761,7 +766,8 @@ public class SettingsManager { if( Files.exists( settingsPath ) ) { - mLog.info( "loading settings file [" + settingsPath.toString() + "]" ); + mLog.info( "SettingsManager - loading settings file [" + + settingsPath.toString() + "]" ); JAXBContext context = null; @@ -781,13 +787,13 @@ public class SettingsManager } catch ( JAXBException e ) { - mLog.error( "jaxb exception while loading " + + mLog.error( "SettingsManager - jaxb exception while loading " + "settings", e ); } } catch ( Exception e ) { - mLog.error( "coulcn't open inputstream to " + + mLog.error( "SettingsManager - coulcn't open inputstream to " + "load settings [" + settingsPath.toString() + "]", e ); } finally @@ -800,7 +806,7 @@ public class SettingsManager } catch ( IOException e ) { - mLog.error( "exception while closing " + + mLog.error( "SettingsManager - exception while closing " + "the settings file inputstream reader", e ); } } @@ -808,7 +814,8 @@ public class SettingsManager } else { - mLog.info( "settings does not exist [" + settingsPath.toString() + "]" ); + mLog.info( "SettingsManager - settings does not exist [" + + settingsPath.toString() + "]" ); } if( mSettings == null ) diff --git a/src/source/mixer/MixerChannelConfiguration.java b/src/source/mixer/MixerChannelConfiguration.java index b8520bcc..0be3b358 100644 --- a/src/source/mixer/MixerChannelConfiguration.java +++ b/src/source/mixer/MixerChannelConfiguration.java @@ -23,14 +23,6 @@ public class MixerChannelConfiguration return mMixerChannel; } - public boolean matches( String mixer, String channels ) - { - return mixer != null && - channels != null && - mMixer.getMixerInfo().getName().contentEquals( mixer ) && - mMixerChannel.name().contentEquals( channels ); - } - public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/source/tuner/TunerChannel.java b/src/source/tuner/TunerChannel.java index 625d7a98..7ff6e32d 100644 --- a/src/source/tuner/TunerChannel.java +++ b/src/source/tuner/TunerChannel.java @@ -97,13 +97,9 @@ public class TunerChannel implements Comparable } @Override - public int compareTo( TunerChannel otherTunerChannel ) + public int compareTo( TunerChannel otherLock ) { - if( this.equals( otherTunerChannel ) ) - { - return 0; - } - else if( mFrequency < otherTunerChannel.getFrequency() ) + if( mFrequency < otherLock.getFrequency() ) { return -1; } diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 207d9eb9..7e844301 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -30,7 +30,6 @@ import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import properties.SystemProperties; import sample.Buffer; import sample.Listener; import sample.SampleType; @@ -62,8 +61,6 @@ public class DFTProcessor implements Listener, private ScheduledExecutorService mScheduler = Executors .newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); - public static final String FRAME_RATE_PROPERTY = "spectral.display.frame.rate"; - private DFTSize mDFTSize = DFTSize.FFT04096; private DFTSize mNewDFTSize = DFTSize.FFT04096; @@ -92,16 +89,7 @@ public class DFTProcessor implements Listener, public DFTProcessor( SampleType sampleType ) { setSampleType( sampleType ); - - loadSettings(); - } - - private void loadSettings() - { - int frameRate = SystemProperties.getInstance() - .get( DFTProcessor.FRAME_RATE_PROPERTY, 20 ); - - setFrameRate( frameRate ); + setFrameRate( 20 ); } public void dispose() @@ -183,10 +171,6 @@ public class DFTProcessor implements Listener, mFrameRate = framesPerSecond; - SystemProperties properties = SystemProperties.getInstance(); - properties.set( FRAME_RATE_PROPERTY, String.valueOf( mFrameRate ) ); - properties.save(); - calculateConsumptionRate(); restart(); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index f8d1acd9..9d76b912 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -54,7 +54,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import playlist.PlaylistManager; -import properties.SystemProperties; import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; @@ -100,10 +99,8 @@ public class SpectralDisplayPanel extends JPanel private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" ); - public static final String FFT_SIZE_PROPERTY = "spectral.display.dft.size"; public static final int NO_ZOOM = 0; public static final int MAX_ZOOM = 6; - private DFTSize mDFTSize = DFTSize.FFT04096; private int mZoom = 0; @@ -158,36 +155,6 @@ public class SpectralDisplayPanel extends JPanel } init(); - - loadSettings(); - } - - private void loadSettings() - { - SystemProperties properties = SystemProperties.getInstance(); - - String rawSize = properties.get( FFT_SIZE_PROPERTY, DFTSize.FFT04096.name() ); - - DFTSize size = null; - - if( rawSize != null ) - { - try - { - size = DFTSize.valueOf( rawSize ); - } - catch( Exception e ) - { - //Do nothing, we couldn't parse the stored value - } - } - - if( size == null ) - { - size = DFTSize.FFT04096; - } - - setDFTSize( size ); } public void dispose() @@ -232,10 +199,6 @@ public class SpectralDisplayPanel extends JPanel mDFTProcessor.setDFTSize( size ); mOverlayPanel.setDFTSize( size ); mDFTSize = size; - - SystemProperties properties = SystemProperties.getInstance(); - properties.set( FFT_SIZE_PROPERTY, size.name() ); - properties.save(); setZoom( 0, 0, 0 ); } diff --git a/src/spectrum/WaterfallPanel.java b/src/spectrum/WaterfallPanel.java index 336e8c6c..2aa7e37a 100644 --- a/src/spectrum/WaterfallPanel.java +++ b/src/spectrum/WaterfallPanel.java @@ -121,8 +121,6 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, mMemoryImageSource.setAnimated( true ); mWaterfallImage = createImage( mMemoryImageSource ); - - repaint(); } /** From 1f654b5b9c574d31db78752c86b86da0d2c912f9 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 14 Feb 2016 11:45:43 -0500 Subject: [PATCH 15/74] Rebase channel/alias model redesign branch on current master (v0.2.0.beta5) --- build/build.xml | 4 +- src/alias/Alias.java | 4 +- src/alias/id/talkgroup/TalkgroupIDEditor.java | 4 - src/audio/AudioManager.java | 584 +++++++++++------- src/audio/output/AudioOutput.java | 38 +- src/audio/output/MonoAudioOutput.java | 157 ++--- src/audio/output/StereoAudioOutput.java | 141 +++-- .../ConfigurationControllerModel.java | 3 - src/controller/ThreadPoolManager.java | 6 +- src/controller/channel/Channel.java | 27 +- src/gui/SDRTrunk.java | 2 +- src/module/ProcessingChain.java | 19 +- src/module/decode/DecoderFactory.java | 9 +- .../decode/mpt1327/MPT1327DecoderState.java | 72 ++- src/module/decode/p25/P25DecoderState.java | 4 +- src/module/decode/state/ChannelState.java | 517 +++++++++------- .../decode/state/ChannelStatePanel.java | 36 +- .../decode/state/DecoderStateEvent.java | 3 +- src/module/decode/state/State.java | 78 ++- src/playlist/PlaylistManager.java | 48 +- .../mixer/MixerChannelConfiguration.java | 8 + src/source/tuner/TunerController.java | 7 +- 22 files changed, 1019 insertions(+), 752 deletions(-) diff --git a/build/build.xml b/build/build.xml index 3667c7ff..27ab0551 100644 --- a/build/build.xml +++ b/build/build.xml @@ -5,9 +5,9 @@ - + - + diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 1c6ddb87..7def2d64 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -34,7 +34,7 @@ public class Alias private String mName; private int mColor; private String mIconName; - private ArrayList mAliasIDs = new ArrayList(); + private List mAliasIDs = new ArrayList(); private List mAliasActions = new ArrayList(); public Alias() @@ -121,7 +121,7 @@ public class Alias mIconName = iconName; } - public ArrayList getId() + public List getId() { return mAliasIDs; } diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index feec6d2d..1feda866 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -36,10 +36,6 @@ public class TalkgroupIDEditor extends JPanel implements ActionListener private TalkgroupIDNode mTalkgroupIDNode; private JTextField mTextTalkgroupID; -<<<<<<< Upstream, based on origin/master -======= - private JLabel mLabelAudio; ->>>>>>> 92e63f7 Adds AliasModel and AliasEvent and support for conversion from legacy playlist format. private String mHelpText = "Enter a formatted talkgroup identifier.\n\n" + "P25 Talkgroup Format: hexadecimal value, 4 characters for " diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index 6fa1a9f7..a8aae418 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -19,14 +19,11 @@ package audio; ******************************************************************************/ import java.util.ArrayList; - import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -37,10 +34,12 @@ import javax.sound.sampled.Mixer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import properties.SystemProperties; import sample.Broadcaster; import sample.Listener; import source.mixer.MixerChannel; import source.mixer.MixerChannelConfiguration; +import source.mixer.MixerManager; import audio.AudioEvent.Type; import audio.output.AudioOutput; import audio.output.MonoAudioOutput; @@ -53,7 +52,9 @@ public class AudioManager implements Listener, IAudioController private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); public static final int AUDIO_TIMEOUT = 2000; //2 seconds - public static final int NO_SOURCE = 0; + + public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; + public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; public static final AudioEvent CONFIGURATION_CHANGE_STARTED = new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_STARTED, null ); @@ -62,49 +63,90 @@ public class AudioManager implements Listener, IAudioController new AudioEvent( Type.AUDIO_CONFIGURATION_CHANGE_COMPLETE, null ); private LinkedTransferQueue mAudioPacketQueue = new LinkedTransferQueue<>(); - private Map mChannelAssignments = new HashMap<>(); - private int mLowestPrioritySource; + private Map mChannelConnectionMap = new HashMap<>(); + private List mAudioOutputConnections = new ArrayList<>(); + private AudioOutputConnection mLowestPriorityConnection; + private int mAvailableConnectionCount; - private List mAvailableAudioOutputs = new ArrayList<>(); private Map mAudioOutputMap = new HashMap<>(); - private List mChannelNames = new ArrayList<>(); private Broadcaster mControllerBroadcaster = new Broadcaster<>(); private ThreadPoolManager mThreadPoolManager; private ScheduledFuture mProcessingTask; + private MixerManager mMixerManager; + private MixerChannelConfiguration mMixerChannelConfiguration; - private MixerChannelConfiguration mMixerChannel; /** * Processes all audio produced by the decoding channels and routes audio * packets to any combination of outputs based on any alias audio routing * options specified by the user. */ - public AudioManager( ThreadPoolManager manager ) + public AudioManager( ThreadPoolManager manager, MixerManager mixerManager ) { mThreadPoolManager = manager; + mMixerManager = mixerManager; - //TODO: save audio system preferences to the settings manager + loadSettings(); + } + + private void loadSettings() + { + MixerChannelConfiguration configuration = null; - /* Use the system default mixer and mono channel as default startup */ - Mixer mixer = AudioSystem.getMixer( null ); + SystemProperties properties = SystemProperties.getInstance(); - if( mixer != null ) + Mixer defaultMixer = AudioSystem.getMixer( null ); + + String mixer = properties.get( AUDIO_MIXER_PROPERTY, defaultMixer.getMixerInfo().getName() ); + + String channels = properties.get( AUDIO_CHANNELS_PROPERTY, MixerChannel.MONO.name() ); + + MixerChannelConfiguration[] mixerConfigurations = mMixerManager.getOutputMixers(); + + for( MixerChannelConfiguration mixerConfig: mixerConfigurations ) { - mMixerChannel = new MixerChannelConfiguration( mixer, MixerChannel.MONO ); + if( mixerConfig.matches( mixer, channels ) ) + { + configuration = mixerConfig; + } + } + + if( configuration == null ) + { + configuration = getDefaultConfiguration(); + } + + try + { + setMixerChannelConfiguration( configuration ); + } + catch( Exception e ) + { + mLog.error( "Couldn't set stored audio mixer/channel configuration" + + " - using default", e ); try { - setMixerChannelConfiguration( mMixerChannel ); - } - catch ( AudioException e ) + setMixerChannelConfiguration( getDefaultConfiguration() ); + } + catch( Exception e2 ) { - mLog.error( "Couldn't create an audio output device", e ); + mLog.error( "Couldn't set default audio mixer/channel " + + "configuration - no audio will be available", e2 ); } } } + private MixerChannelConfiguration getDefaultConfiguration() + { + /* Use the system default mixer and mono channel as default startup */ + Mixer defaultMixer = AudioSystem.getMixer( null ); + + return new MixerChannelConfiguration( defaultMixer, MixerChannel.MONO ); + } + public void dispose() { if( mThreadPoolManager != null && mProcessingTask != null ) @@ -116,6 +158,15 @@ public class AudioManager implements Listener, IAudioController mThreadPoolManager = null; mProcessingTask = null; + + mChannelConnectionMap.clear(); + + for( AudioOutputConnection connection: mAudioOutputConnections ) + { + connection.dispose(); + } + + mAudioOutputConnections.clear(); } @Override @@ -124,207 +175,44 @@ public class AudioManager implements Listener, IAudioController mAudioPacketQueue.add( packet ); } - public class AudioPacketProcessor implements Runnable - { - @Override - public void run() - { - removeCompletedChannelAssignments(); - - if( mAudioPacketQueue != null ) - { - List packets = new ArrayList(); - - mAudioPacketQueue.drainTo( packets ); - - for( AudioPacket packet: packets ) - { - /* Don't process any packet's marked as do not monitor */ - if( !packet.getAudioMetadata().isDoNotMonitor() ) - { - int source = packet.getAudioMetadata().getSource(); - - if( packet.getType() != AudioPacket.Type.END ) - { - /* Existing channel assignment */ - if( mChannelAssignments.containsKey( source ) ) - { - mChannelAssignments.get( source ).getAudioOutput() - .receive( packet ); - } - /* Start a new channel assignment */ - else if( !mAvailableAudioOutputs.isEmpty() ) - { - AudioOutput channel = mAvailableAudioOutputs.remove( 0 ); - - mChannelAssignments.put( packet.getAudioMetadata().getSource(), - new AudioChannelAssignment( channel, packet.getAudioMetadata().getPriority() ) ); - - updateLowestPriorityAssignment(); - - channel.receive( packet ); - } - /* Override the lowest priority assignment */ - else - { - if( mLowestPrioritySource > -1 ) - { - int priority = packet.getAudioMetadata().getPriority(); - - if( packet.getAudioMetadata().isSelected() || - priority < mChannelAssignments - .get( mLowestPrioritySource ).getPriority() ) - { - AudioChannelAssignment assignment = - mChannelAssignments.remove( mLowestPrioritySource ); - - if( assignment != null ) - { - assignment.setPriority( priority ); - - int overrideSource = packet.getAudioMetadata().getSource(); - - mChannelAssignments.put( overrideSource, assignment ); - - mLowestPrioritySource = overrideSource; - - assignment.getAudioOutput().receive( packet ); - } - else - { - mLowestPrioritySource = -1; - } - } - } - } - } - } - } - } - } - } - /** - * Checks each audio channel assignment and removes any completed channels. + * Checks each audio channel assignment and disconnects any inactive connections */ - private void removeCompletedChannelAssignments() + private void disconnectInactiveChannelAssignments() { - if( !mChannelAssignments.isEmpty() ) + boolean changed = false; + + for( AudioOutputConnection connection: mAudioOutputConnections ) { - Iterator> it = - mChannelAssignments.entrySet().iterator(); - - Entry entry; - - boolean removed = false; - - while( it.hasNext() ) + if( connection.isInactive() && + mChannelConnectionMap.containsKey( connection.getSource() ) ) { - entry = it.next(); - - if( entry.getValue().isComplete() ) - { - mAvailableAudioOutputs.add( entry.getValue().getAudioOutput() ); - entry.getValue().dispose(); - it.remove(); - removed = true; - } - } - - if( removed ) - { - updateLowestPriorityAssignment(); + mChannelConnectionMap.remove( connection.getSource() ); + connection.disconnect(); + changed = true; } } + + if( changed ) + { + updateLowestPriorityAssignment(); + } } /** - * Identifies the lowest priority channel assignment ... the one that can - * be overridden with a higher priority call + * Identifies the lowest priority channel connection */ private void updateLowestPriorityAssignment() { - int lowestPriority = -1; + mLowestPriorityConnection = null; - mLowestPrioritySource = -1; - - for( int source: mChannelAssignments.keySet() ) + for( AudioOutputConnection connection: mAudioOutputConnections ) { - AudioChannelAssignment assignment = mChannelAssignments.get( source ); - - if( assignment.getPriority() > lowestPriority ) + if( connection.isConnected() && + ( mLowestPriorityConnection == null || + mLowestPriorityConnection.getPriority() > connection.getPriority() ) ) { - lowestPriority = assignment.getPriority(); - - mLowestPrioritySource = source; - } - } - } - - public void endAllAudioAssignments() - { - for( AudioOutput output: mAudioOutputMap.values() ) - { - output.receive( new AudioPacket( AudioPacket.Type.END, null ) ); - } - } - - public class AudioChannelAssignment implements Listener - { - private static final long CALL_END_DELAY = 2000; //2 seconds - - private AudioOutput mAudioOutput; - private int mPriority; - private long mAudioStoppedTime = System.currentTimeMillis(); - - public AudioChannelAssignment( AudioOutput audioOutput, int priority ) - { - mAudioOutput = audioOutput; - mPriority = priority; - - mAudioOutput.addAudioEventListener( this ); - } - - public boolean isComplete() - { - return mAudioStoppedTime != Long.MAX_VALUE && - mAudioStoppedTime + CALL_END_DELAY < System.currentTimeMillis(); - } - - public void dispose() - { - mAudioOutput.removeAudioEventListener( this ); - mAudioOutput.reset(); - mAudioOutput = null; - } - - public AudioOutput getAudioOutput() - { - return mAudioOutput; - } - - public int getPriority() - { - return mPriority; - } - - public void setPriority( int priority ) - { - mPriority = priority; - } - - @Override - public void receive( AudioEvent event ) - { - switch( event.getType() ) - { - case AUDIO_STARTED: - case AUDIO_STOPPED: - case AUDIO_CONTINUATION: - mAudioStoppedTime = System.currentTimeMillis(); - break; - default: - break; + mLowestPriorityConnection = connection; } } } @@ -342,41 +230,29 @@ public class AudioManager implements Listener, IAudioController { mThreadPoolManager.cancel( mProcessingTask ); } - - endAllAudioAssignments(); - for( Entry e: mChannelAssignments.entrySet() ) - { - e.getValue().getAudioOutput().dispose(); - } - mChannelAssignments.clear(); + disposeCurrentConfiguration(); - for( AudioOutput output: mAvailableAudioOutputs ) - { - output.dispose(); - } - mAvailableAudioOutputs.clear(); - - mChannelNames.clear(); - mAudioOutputMap.clear(); - switch( entry.getMixerChannel() ) { case MONO: - AudioOutput mono = new MonoAudioOutput( entry.getMixer() ); - mAvailableAudioOutputs.add( mono ); - mChannelNames.add( mono.getChannelName() ); + AudioOutput mono = new MonoAudioOutput( mThreadPoolManager, + entry.getMixer() ); + mAudioOutputConnections.add( new AudioOutputConnection( mono ) ); + mAvailableConnectionCount++; mAudioOutputMap.put( mono.getChannelName(), mono ); break; case STEREO: - AudioOutput left = new StereoAudioOutput( entry.getMixer(), MixerChannel.LEFT ); - mAvailableAudioOutputs.add( left ); - mChannelNames.add( left.getChannelName() ); + AudioOutput left = new StereoAudioOutput( mThreadPoolManager, + entry.getMixer(), MixerChannel.LEFT ); + mAudioOutputConnections.add( new AudioOutputConnection( left ) ); + mAvailableConnectionCount++; mAudioOutputMap.put( left.getChannelName(), left ); - AudioOutput right = new StereoAudioOutput( entry.getMixer(), MixerChannel.RIGHT ); - mAvailableAudioOutputs.add( right ); - mChannelNames.add( right.getChannelName() ); + AudioOutput right = new StereoAudioOutput( mThreadPoolManager, + entry.getMixer(), MixerChannel.RIGHT ); + mAudioOutputConnections.add( new AudioOutputConnection( right ) ); + mAvailableConnectionCount++; mAudioOutputMap.put( right.getChannelName(), right ); break; default: @@ -389,6 +265,12 @@ public class AudioManager implements Listener, IAudioController TimeUnit.MILLISECONDS ); mControllerBroadcaster.broadcast( CONFIGURATION_CHANGE_COMPLETE ); + + SystemProperties properties = SystemProperties.getInstance(); + + properties.set( AUDIO_MIXER_PROPERTY, entry.getMixer().getMixerInfo().getName() ); + properties.set( AUDIO_CHANNELS_PROPERTY, entry.getMixerChannel().name() ); + properties.save(); } } @@ -418,7 +300,7 @@ public class AudioManager implements Listener, IAudioController @Override public MixerChannelConfiguration getMixerChannelConfiguration() throws AudioException { - return mMixerChannel; + return mMixerChannelConfiguration; } @Override @@ -449,4 +331,244 @@ public class AudioManager implements Listener, IAudioController { mControllerBroadcaster.removeListener( listener ); } -} + + /** + * Returns an audio output connection for the packet if one is + * available, or overrides an existing lower priority connection. + * Returns null if no connection is available for the audio packet. + * + * @param packet from a source + * @return an audio output connection or null + */ + private AudioOutputConnection getConnection( AudioPacket packet ) + { + int source = packet.getAudioMetadata().getSource(); + + //Use an existing connection + if( mChannelConnectionMap.containsKey( source ) ) + { + return mChannelConnectionMap.get( source ); + } + + //Connect to and use an available connection + if( mAvailableConnectionCount > 0 ) + { + for( AudioOutputConnection connection: mAudioOutputConnections ) + { + if( connection.isDisconnected() ) + { + connection.connect( source ); + mChannelConnectionMap.put( source, connection ); + return connection; + } + } + } + //Preempt an existing connection and connect it to this higher priority source + else //Check for a channel priority override + { + int priority = packet.getAudioMetadata().getPriority(); + + if( mLowestPriorityConnection != null && + priority < mLowestPriorityConnection.getPriority() ) + { + AudioOutputConnection connection = mLowestPriorityConnection; + + mChannelConnectionMap.remove( connection.getSource() ); + + connection.connect( source ); + + mChannelConnectionMap.put( source, connection ); + + updateLowestPriorityAssignment(); + + return connection; + } + } + + return null; + } + + public class AudioPacketProcessor implements Runnable + { + @Override + public void run() + { + try + { + disconnectInactiveChannelAssignments(); + + if( mAudioPacketQueue != null ) + { + List packets = new ArrayList(); + + mAudioPacketQueue.drainTo( packets ); + + for( AudioPacket packet: packets ) + { + /* Don't process any packet's marked as do not monitor */ + if( !packet.getAudioMetadata().isDoNotMonitor() && + packet.getType() == AudioPacket.Type.AUDIO ) + { + AudioOutputConnection connection = getConnection( packet ); + + if( connection != null ) + { + connection.receive( packet ); + } + } + } + } + } + catch( Exception e ) + { + mLog.error( "Encountered error while processing audio packets", e ); + } + } + } + + /** + * Audio output connection manages a connection between a source and an audio + * output and maintains current state information about the audio activity + * received form the source. + */ + public class AudioOutputConnection implements Listener + { + private static final long CALL_END_DELAY = 2000; //2 seconds + private static final int DISCONNECTED = -1; + + private AudioOutput mAudioOutput; + private int mPriority = 0; + private long mLastActivity = System.currentTimeMillis(); + private int mSource = DISCONNECTED; + + public AudioOutputConnection( AudioOutput audioOutput ) + { + mAudioOutput = audioOutput; + mAudioOutput.addAudioEventListener( this ); + } + + public void receive( AudioPacket packet ) + { + if( packet.hasAudioMetadata() && + packet.getAudioMetadata().getSource() == mSource ) + { + int priority = packet.getAudioMetadata().getPriority(); + + if( mPriority != priority ) + { + mPriority = priority; + updateLowestPriorityAssignment(); + } + + updateTimestamp(); + + if( mAudioOutput != null ) + { + mAudioOutput.receive( packet ); + } + } + else + { + if( packet.hasAudioMetadata() ) + { + mLog.error( "Received audio packet from channel source [" + + packet.getAudioMetadata().getSource() + + "] however this assignment is currently connected " + + "to channel source [" + mSource + "]" ); + } + else + { + mLog.error( "Received audio packet with no metadata - " + + "cannot route audio packet" ); + } + } + } + + private void updateTimestamp() + { + mLastActivity = System.currentTimeMillis(); + } + + /** + * Terminates the audio output and prepares this connection for disposal + */ + public void dispose() + { + mAudioOutput.dispose(); + mAudioOutput = null; + } + + /** + * Indicates if this assignment is currently disconnected from a channel source + */ + public boolean isDisconnected() + { + return mSource == DISCONNECTED; + } + + /** + * Indicates if this assignment is currently connected to a channel source + */ + public boolean isConnected() + { + return !isDisconnected(); + } + + /** + * Connects this assignment to the indicated source so that audio + * packets from this source can be sent to the audio output + */ + public void connect( int source ) + { + mSource = source; + updateTimestamp(); + } + + /** + * Currently connected source or -1 if disconnected + */ + public int getSource() + { + return mSource; + } + + /** + * Disconnects this assignment from the source and prevents any audio + * from being routed to the audio output until another source is assigned + */ + public void disconnect() + { + mSource = DISCONNECTED; + } + + /** + * Indicates if audio output is current inactive, meaning that the + * audio output hasn't recently processed any audio packets. + */ + public boolean isInactive() + { + return mLastActivity != Long.MAX_VALUE && + mLastActivity + CALL_END_DELAY < System.currentTimeMillis(); + } + + public int getPriority() + { + return mPriority; + } + + @Override + public void receive( AudioEvent event ) + { + switch( event.getType() ) + { + case AUDIO_STARTED: + case AUDIO_STOPPED: + case AUDIO_CONTINUATION: + updateTimestamp(); + break; + default: + break; + } + } + } +} \ No newline at end of file diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java index d9506472..31f4300f 100644 --- a/src/audio/output/AudioOutput.java +++ b/src/audio/output/AudioOutput.java @@ -1,10 +1,9 @@ package audio.output; import java.util.concurrent.LinkedTransferQueue; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -12,23 +11,26 @@ import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; import audio.AudioEvent; -import audio.AudioEvent.Type; import audio.AudioPacket; import audio.metadata.AudioMetadata; +import controller.ThreadPoolManager; -public abstract class AudioOutput implements Listener +public abstract class AudioOutput implements Listener, LineListener { private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); - private AtomicBoolean mSquelched = new AtomicBoolean( false ); private Listener mAudioMetadataListener; - protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); protected LinkedTransferQueue mBuffer = new LinkedTransferQueue<>(); - protected ScheduledExecutorService mExecutorService; + protected ThreadPoolManager mThreadPoolManager; protected boolean mCanProcessAudio; protected long mLastActivity; + public AudioOutput( ThreadPoolManager threadPoolManager ) + { + mThreadPoolManager = threadPoolManager; + } + public void reset() { broadcast( new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, @@ -41,14 +43,11 @@ public abstract class AudioOutput implements Listener mBuffer.clear(); - if( mExecutorService != null ) - { - mExecutorService.shutdown(); - } - mAudioEventBroadcaster.dispose(); mAudioEventBroadcaster = null; mAudioMetadataListener = null; + + mThreadPoolManager = null; } public abstract String getChannelName(); @@ -72,19 +71,6 @@ public abstract class AudioOutput implements Listener mAudioEventBroadcaster.broadcast( audioEvent ); } - public void setSquelched( boolean squelched ) - { - mSquelched.set( squelched ); - - broadcast( new AudioEvent( squelched ? Type.AUDIO_SQUELCHED : - Type.AUDIO_UNSQUELCHED, getChannelName() ) ); - } - - public boolean isSquelched() - { - return mSquelched.get(); - } - /** * Registers a single listener to receive audio start and audio stop events */ @@ -130,4 +116,4 @@ public abstract class AudioOutput implements Listener mBuffer.add( packet ); } } -} +} \ No newline at end of file diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java index 76bf2324..f1aad5cd 100644 --- a/src/audio/output/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -5,13 +5,14 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; @@ -24,8 +25,8 @@ import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; import audio.AudioPacket.Type; -import audio.output.StereoAudioOutput.QueueProcessor; -import controller.NamingThreadFactory; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; /** * Mono Audio output with automatic flow control based on the availability of @@ -50,14 +51,14 @@ public class MonoAudioOutput extends AudioOutput private SourceDataLine mOutput; private FloatControl mGainControl; private BooleanControl mMuteControl; - private AudioEvent mAudioStartEvent; private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; + private ScheduledFuture mProcessorTask; - public MonoAudioOutput( Mixer mixer ) + public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer ) { - super(); + super( threadPoolManager ); mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, getChannelName() ); @@ -74,15 +75,8 @@ public class MonoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE ); - + mOutput.addLineListener( this ); mCanProcessAudio = true; - - mExecutorService = Executors.newSingleThreadScheduledExecutor( - new NamingThreadFactory( "audio (mono) output" ) ); - - /* Run the queue processor task every 40 milliseconds */ - mExecutorService.scheduleAtFixedRate( new QueueProcessor(), - 0, 40, TimeUnit.MILLISECONDS ); } } catch ( LineUnavailableException e ) @@ -115,12 +109,10 @@ public class MonoAudioOutput extends AudioOutput mixer.getMixerInfo().getName() + "]" ); } - mExecutorService = Executors.newSingleThreadScheduledExecutor( - new NamingThreadFactory( "audio (mono) output" ) ); - /* Run the queue processor task every 40 milliseconds */ - mExecutorService.scheduleAtFixedRate( new QueueProcessor(), - 0, 40, TimeUnit.MILLISECONDS ); + mProcessorTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new QueueProcessor(), + 40, TimeUnit.MILLISECONDS ); } } @@ -132,102 +124,108 @@ public class MonoAudioOutput extends AudioOutput public void dispose() { + if( mProcessorTask != null && mThreadPoolManager != null ) + { + mThreadPoolManager.cancel( mProcessorTask ); + } + + mProcessorTask = null; + super.dispose(); if( mOutput != null ) { mOutput.close(); } + + mOutput = null; + mGainControl = null; + mMuteControl = null; } public class QueueProcessor implements Runnable { private AtomicBoolean mProcessing = new AtomicBoolean(); - public QueueProcessor() - { - } - @Override public void run() { - /* The mProcessing flag ensures that only one instance of the - * processor can run at any given time */ - if( mProcessing.compareAndSet( false, true ) ) + try { - List packets = new ArrayList(); - - mBuffer.drainTo( packets ); - - if( packets.size() > 0 ) + /* The mProcessing flag ensures that only one instance of the + * processor can run at any given time */ + if( mProcessing.compareAndSet( false, true ) ) { - broadcast( mAudioContinuationEvent ); + List packets = new ArrayList(); - for( AudioPacket packet: packets ) + mBuffer.drainTo( packets ); + + if( packets.size() > 0 ) { - if( mCanProcessAudio ) + for( AudioPacket packet: packets ) { - if( isSquelched() ) + broadcast( mAudioContinuationEvent ); + + if( mCanProcessAudio ) { - checkStop(); - } - else if( packet.getType() == Type.AUDIO ) - { - float[] samples = packet.getAudioBuffer().getSamples(); - - /* Little-endian byte buffer */ - ByteBuffer buffer = - ByteBuffer.allocate( samples.length * 2 ) - .order( ByteOrder.LITTLE_ENDIAN ); - - ShortBuffer shortBuffer = buffer.asShortBuffer(); - - for( float sample: samples ) + if( packet.getType() == Type.AUDIO ) { - shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); - } - - mOutput.write( buffer.array(), 0, - buffer.array().length ); - - checkStart(); - - broadcast( packet.getAudioMetadata() ); + float[] samples = packet.getAudioBuffer().getSamples(); + + /* Little-endian byte buffer */ + ByteBuffer buffer = + ByteBuffer.allocate( samples.length * 2 ) + .order( ByteOrder.LITTLE_ENDIAN ); + + ShortBuffer shortBuffer = buffer.asShortBuffer(); - mLastActivity = System.currentTimeMillis(); + for( float sample: samples ) + { + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); + } + + //This is a blocking write + mOutput.write( buffer.array(), 0, buffer.array().length ); + + checkStart(); + + broadcast( packet.getAudioMetadata() ); + + mLastActivity = System.currentTimeMillis(); + } } } } + else + { + checkStop(); + } + + mProcessing.set( false ); } - else - { - checkStop(); - } - - mProcessing.set( false ); + } + catch( Exception e ) + { + mLog.error( "Error while processing audio buffers", e ); } } private void checkStart() { - if( mCanProcessAudio && !isSquelched() && + if( mCanProcessAudio && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); - - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) + mOutput.available() >= STOP_THRESHOLD ) { mOutput.drain(); mOutput.stop(); - - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } @@ -266,4 +264,19 @@ public class MonoAudioOutput extends AudioOutput { return mGainControl != null; } -} + + @Override + public void update( LineEvent event ) + { + LineEvent.Type type = event.getType(); + + if( type == LineEvent.Type.START ) + { + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); + } + else if( type == LineEvent.Type.STOP ) + { + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); + } + } +} \ No newline at end of file diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java index 81892ca5..8a624d18 100644 --- a/src/audio/output/StereoAudioOutput.java +++ b/src/audio/output/StereoAudioOutput.java @@ -5,13 +5,14 @@ import java.nio.ByteOrder; import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.BooleanControl; import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; import javax.sound.sampled.SourceDataLine; @@ -24,10 +25,11 @@ import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; import audio.AudioPacket.Type; -import controller.NamingThreadFactory; +import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; /** - * Mono Audio output in stereo format with automatic flow control based on the + * Stereo Audio output in stereo format with automatic flow control based on the * availability of audio data packets. */ public class StereoAudioOutput extends AudioOutput @@ -39,9 +41,9 @@ public class StereoAudioOutput extends AudioOutput private final static int BUFFER_SIZE = 96000; /* The queue processor will run every 20 milliseconds checking for inbound - * audio and automatically starting or stopping audio playback. We set the + * audio and automatically start or stop audio playback. We set the * data line available space threshold to start when we're 20 ms away from - * being full, and the stop threshold when 20 ms away from being empty */ + * being full and the stop threshold when 20 ms away from being empty */ private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25; private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS; private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS; @@ -49,16 +51,16 @@ public class StereoAudioOutput extends AudioOutput private SourceDataLine mOutput; private FloatControl mGainControl; private BooleanControl mMuteControl; - private AudioEvent mAudioStartEvent; private AudioEvent mAudioStopEvent; private AudioEvent mAudioContinuationEvent; - private MixerChannel mMixerChannel; + private ScheduledFuture mProcessorTask; - public StereoAudioOutput( Mixer mixer, MixerChannel channel ) + public StereoAudioOutput( ThreadPoolManager threadPoolManager, + Mixer mixer, MixerChannel channel ) { - super(); + super( threadPoolManager ); assert( channel == MixerChannel.LEFT || channel == MixerChannel.RIGHT ); @@ -79,6 +81,7 @@ public class StereoAudioOutput extends AudioOutput if( mOutput != null ) { mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE ); + mOutput.addLineListener( this ); mCanProcessAudio = true; } } @@ -113,12 +116,10 @@ public class StereoAudioOutput extends AudioOutput mixer.getMixerInfo().getName() + " | " + channel.name() + "]" ); } - mExecutorService = Executors.newSingleThreadScheduledExecutor( - new NamingThreadFactory( "audio (stereo) output" ) ); - /* Run the queue processor task every 40 milliseconds */ - mExecutorService.scheduleAtFixedRate( new QueueProcessor(), - 0, 40, TimeUnit.MILLISECONDS ); + mProcessorTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new QueueProcessor(), + 40, TimeUnit.MILLISECONDS ); } } @@ -130,20 +131,30 @@ public class StereoAudioOutput extends AudioOutput public void dispose() { + if( mProcessorTask != null && mThreadPoolManager != null ) + { + mThreadPoolManager.cancel( mProcessorTask ); + } + + mProcessorTask = null; + super.dispose(); if( mOutput != null ) { mOutput.close(); } + + mOutput = null; + mGainControl = null; + mMuteControl = null; } private ByteBuffer convert( float[] samples ) { /* Little-endian byte buffer */ - ByteBuffer buffer = - ByteBuffer.allocate( samples.length * 4 ) - .order( ByteOrder.LITTLE_ENDIAN ); + ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 ) + .order( ByteOrder.LITTLE_ENDIAN ); ShortBuffer shortBuffer = buffer.asShortBuffer(); @@ -178,72 +189,72 @@ public class StereoAudioOutput extends AudioOutput @Override public void run() { - /* The mProcessing flag ensures that only one instance of the - * processor can run at any given time */ - if( mProcessing.compareAndSet( false, true ) ) + try { - List packets = new ArrayList(); - - mBuffer.drainTo( packets ); - - if( packets.size() > 0 ) + /* The mProcessing flag ensures that only one instance of the + * processor can run at any given time */ + if( mProcessing.compareAndSet( false, true ) ) { - broadcast( mAudioContinuationEvent ); + List packets = new ArrayList(); - for( AudioPacket packet: packets ) + mBuffer.drainTo( packets ); + + if( packets.size() > 0 ) { - if( mCanProcessAudio ) + + for( AudioPacket packet: packets ) { - if( isSquelched() ) + broadcast( mAudioContinuationEvent ); + + if( mCanProcessAudio ) { - checkStop(); - } - else if( packet.getType() == Type.AUDIO ) - { - ByteBuffer buffer = convert( - packet.getAudioBuffer().getSamples() ); - - mOutput.write( buffer.array(), 0, - buffer.array().length ); - - checkStart(); - - broadcast( packet.getAudioMetadata() ); - - mLastActivity = System.currentTimeMillis(); + if( packet.getType() == Type.AUDIO ) + { + ByteBuffer buffer = convert( + packet.getAudioBuffer().getSamples() ); + + mOutput.write( buffer.array(), 0, + buffer.array().length ); + + checkStart(); + + broadcast( packet.getAudioMetadata() ); + + mLastActivity = System.currentTimeMillis(); + } } } } + else + { + checkStop(); + } + + mProcessing.set( false ); } - else - { - checkStop(); - } - - mProcessing.set( false ); + } + catch( Exception e ) + { + mLog.error( "Error while processing audio buffers", e ); } } private void checkStart() { - if( mCanProcessAudio && !isSquelched() && + if( mCanProcessAudio && !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) { mOutput.start(); - - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); } } private void checkStop() { if( mCanProcessAudio && mOutput.isRunning() && - ( mOutput.available() >= STOP_THRESHOLD || isSquelched() ) ) + mOutput.available() >= STOP_THRESHOLD ) { mOutput.drain(); mOutput.stop(); - - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); } } } @@ -282,4 +293,20 @@ public class StereoAudioOutput extends AudioOutput { return mGainControl != null; } -} + + + @Override + public void update( LineEvent event ) + { + LineEvent.Type type = event.getType(); + + if( type == LineEvent.Type.START ) + { + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); + } + else if( type == LineEvent.Type.STOP ) + { + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); + } + } +} \ No newline at end of file diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java index 692ee7ee..22d3232c 100644 --- a/src/controller/ConfigurationControllerModel.java +++ b/src/controller/ConfigurationControllerModel.java @@ -64,7 +64,6 @@ public class ConfigurationControllerModel extends DefaultTreeModel private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; private SourceManager mSourceManager; - private ChannelModel mChannelModel; /** * Implements a merged system controller and (tree) model. The user will @@ -80,8 +79,6 @@ public class ConfigurationControllerModel extends DefaultTreeModel { super( new BaseNode( null ) ); - mChannelModel = channelModel; - mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; mPlaylistManager = playlistManager; diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index c5527970..c076422a 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -74,11 +74,13 @@ public class ThreadPoolManager mExecutor.schedule( command, delay, unit ); } - public void cancel( ScheduledFuture task ) + public boolean cancel( ScheduledFuture task ) { - task.cancel( true ); + boolean success = task.cancel( true ); mTasks.remove( task ); + + return success; } public int getTaskCount( ThreadType type ) diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index a86d4aae..be2bf97f 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -78,14 +78,13 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor private boolean mEnabled; private boolean mSelected; - private TunerChannel mTunerChannel = null; private ChannelType mChannelType = ChannelType.STANDARD; private int mChannelID; private int mChannelFrequencyCorrection = 0; - + /** * Channel represents a complete set of configurations needed to setup and * manage a processing chain and/or manage as a set of business objects that @@ -112,8 +111,6 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor /** * Constructs a new standard channel with a default name of "Channel" */ - - //TODO: remove this public Channel() { mChannelID = UNIQUE_ID++; @@ -163,7 +160,6 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor return channel; } -//TODO: remove this /** * Unique identifier for this channel. Value is transient (not persisted). */ @@ -442,4 +438,25 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor return tunerChannel != null && tunerChannel.overlaps( minimum, maximum ); } + + /** + * Convenience method to make the current channel frequency correction value + * available for use outside of a constructed processing chain while maintaining + * linkage to the source channel. This hack allows the correction value to + * be used to visually show in the spectral display. + */ + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + if( event.getEvent() == Event.NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE ) + { + mChannelFrequencyCorrection = event.getValue().intValue(); + } + } + + @XmlTransient + public int getChannelFrequencyCorrection() + { + return mChannelFrequencyCorrection; + } } diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index db22429c..ef21d641 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -140,7 +140,7 @@ public class SDRTrunk AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); channelProcessingManager.addMessageListener( aliasActionManager ); - AudioManager audioManager = new AudioManager( threadPoolManager ); + AudioManager audioManager = new AudioManager( threadPoolManager, sourceManager.getMixerManager() ); channelProcessingManager.addAudioPacketListener( audioManager ); MapService mapService = new MapService( mSettingsManager ); diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 68c94256..e48e644e 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -108,7 +108,8 @@ public class ProcessingChain implements IChannelEventListener private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); -// private ThreadPoolManager mThreadPoolManager; + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mBufferProcessingTask; private AtomicBoolean mRunning = new AtomicBoolean(); protected Source mSource; @@ -135,11 +136,6 @@ public class ProcessingChain implements IChannelEventListener addCallEventListener( mCallEventModel ); } - public MessageActivityModel getMessageActivityModel() - { - return mMessageActivityModel; - } - public CallEventModel getCallEventModel() { return mCallEventModel; @@ -150,6 +146,11 @@ public class ProcessingChain implements IChannelEventListener return mChannelState; } + public MessageActivityModel getMessageActivityModel() + { + return mMessageActivityModel; + } + public void setMessageActivityModel( MessageActivityModel model ) { mMessageActivityModel = model; @@ -526,6 +527,12 @@ public class ProcessingChain implements IChannelEventListener mSource = null; } + if( mBufferProcessingTask != null ) + { + mThreadPoolManager.cancel( mBufferProcessingTask ); + mBufferProcessingTask = null; + } + /* Stop each of the modules */ for( Module module: mModules ) { diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 02a41441..447e7865 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -211,11 +211,12 @@ public class DecoderFactory if( channelType == ChannelType.STANDARD ) { + long timeout = mptConfig.getCallTimeout() * 1000; //convert to milliseconds modules.add( new TrafficChannelManager( channelModel, channelProcessingManager, - decodeConfig, recordConfig, channel.getSystem(), - channel.getSite(), ( aliasList != null ? aliasList.getName() : null ), timeout, - mptConfig.getTrafficChannelPoolSize() ) ); + decodeConfig, recordConfig, channel.getSystem(), + channel.getSite(), ( aliasList != null ? aliasList.getName() : null ), timeout, + mptConfig.getTrafficChannelPoolSize() ) ); } modules.add( new FMDemodulatorModule( iqPass, iqStop ) ); @@ -256,7 +257,7 @@ public class DecoderFactory if( channelType == ChannelType.STANDARD ) { //Set call timeout to 3 seconds for P25 traffic channels - timeout = 3000; + long timeout = 3000; modules.add( new TrafficChannelManager( channelModel, channelProcessingManager, decodeConfig, diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index ad40aa52..a5b94f27 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -26,9 +26,11 @@ import java.util.TreeSet; import message.Message; import module.decode.DecoderType; +import module.decode.config.DecodeConfiguration; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; import module.decode.mpt1327.MPT1327Message.IdentType; +import module.decode.state.ChangeChannelTimeoutEvent; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; @@ -41,6 +43,8 @@ import org.slf4j.LoggerFactory; import alias.Alias; import alias.AliasList; +import audio.metadata.Metadata; +import audio.metadata.MetadataType; import controller.channel.Channel.ChannelType; import controller.channel.map.ChannelMap; @@ -60,15 +64,18 @@ public class MPT1327DecoderState extends DecoderState private ChannelMap mChannelMap; private long mFrequency = 0; + private long mCallTimeout; - public MPT1327DecoderState( AliasList aliasList, ChannelMap channelMap, - ChannelType channelType ) + public MPT1327DecoderState( AliasList aliasList, + ChannelMap channelMap, + ChannelType channelType, + long callTimeout ) { super( aliasList ); mChannelMap = channelMap; - mChannelType = channelType; + mCallTimeout = callTimeout; } @Override @@ -252,8 +259,42 @@ public class MPT1327DecoderState extends DecoderState /* Traffic Channel Events */ case CLEAR: + mChannelNumber = mpt.getChannel(); + + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); + break; case MAINT: - broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + if( mChannelType == ChannelType.STANDARD ) + { + /** + * When we receive a MAINT message and we're configured + * as a standard channel, we need to apply the call + * timeout specified by the user. Otherwise we'll + * be using the shorter default call timeout + */ + broadcast( new ChangeChannelTimeoutEvent( this, + mChannelType, mCallTimeout ) ); + + if( mCurrentCallEvent == null ) + { + mCurrentCallEvent = new MPT1327CallEvent.Builder( CallEventType.CALL ) + .aliasList( getAliasList() ) + .channel( String.valueOf( mChannelNumber ) ) + .details( "MONITORED TRAFFIC CHANNEL" ) + .frequency( mFrequency ) + .to( mpt.getToID() ) + .build(); + + broadcast( mCurrentCallEvent ); + } + + broadcast(new Metadata(MetadataType.TO, + mpt.getToID(), mpt.getToIDAlias(), true)); + + broadcast( new DecoderStateEvent( this, Event.START, State.CALL ) ); + + setToTalkgroup( mpt.getToID() ); + } break; default: break; @@ -276,6 +317,25 @@ public class MPT1327DecoderState extends DecoderState mToTalkgroup = null; broadcast( ChangedAttribute.TO_TALKGROUP ); + + /** + * If this is a standard channel, reset the fade timeout to the default + * timeout. Once processing is underway, if we get a MAINT message, + * this indicates we're processing a traffic channel as a standard + * channel, so we'll issue a different call timeout then. + */ + if( mChannelType == ChannelType.STANDARD ) + { + broadcast( new ChangeChannelTimeoutEvent( this, mChannelType, + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000 ) ); + + if( mCurrentCallEvent != null ) + { + mCurrentCallEvent.end(); + broadcast( mCurrentCallEvent ); + mCurrentCallEvent = null; + } + } } public String getSite() @@ -552,4 +612,4 @@ public class MPT1327DecoderState extends DecoderState break; } } -} +} \ No newline at end of file diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 0ffe62ce..55340dba 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -437,7 +437,7 @@ public class P25DecoderState extends DecoderState { if( tdulc.getOpcode() == LinkControlOpcode.CALL_TERMINATION_OR_CANCELLATION ) { - broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); if( mCurrentCallEvent != null ) { @@ -1057,7 +1057,7 @@ public class P25DecoderState extends DecoderState } break; case CALL_TERMINATION_OR_CANCELLATION: - broadcast( new DecoderStateEvent( this, Event.END, State.END ) ); + broadcast( new DecoderStateEvent( this, Event.END, State.FADE ) ); mCurrentCallEvent = null; if( !( ldu instanceof CallTermination ) ) diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java index 3b538349..450ed92d 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/module/decode/state/ChannelState.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,20 +17,14 @@ ******************************************************************************/ package module.decode.state; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import module.Module; -import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEvent; -import module.decode.event.ICallEventProvider; -import module.decode.state.DecoderStateEvent.Event; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; import audio.metadata.IMetadataProvider; import audio.metadata.Metadata; import audio.metadata.MetadataReset; @@ -40,38 +34,13 @@ import audio.squelch.SquelchState; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import controller.channel.Channel.ChannelType; +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent.Event; +import sample.Listener; -/** - * Channel state tracks the overall state of all processing modules and decoders - * configured for the channel and provides squelch control events. - * - * Primary channel state provides a state machine for tracking decode events - * during a voice or data call through it's life cycle of CALL/DATA, FADE, END, - * and then back to an IDLE state. This class also provides squelch control - * to support any audio processing. - * - * State Descriptions: - * IDLE No voice or data call activity - * CALL/DATA A voice or data call has started, or is continuing - * CONTROL Control channel - * FADE The phase after a voice or data call when either an explicit - * call end has been received, or when no new signalling updates - * have been received, and the fade timer has expired. This phase - * allows for gui updates to signal to the user that the call is - * ended, while continuing to display the call details for the user - * END The phase after the voice or data call FADE, when the call is - * now over, and the gui should be reset to IDLE. - * - * When a call is started, invoke mTimerService.startFadeTimer() to start the - * call fade monitoring timer process that will automatically signal a call end - * once no more call signalling has been detected after the fade timeout has - * occurred, by invoking the fade() method. - * - * When a call ends, invoke the mTimerService.stopFadeTimer() and then invoke - * the mTimerService.startResetTimer(). Once the reset timeout occurs, - * the reset() method will be called, allowing the channel state to perform - * any reset actions, like clearing status and id labels on any view panels. - */ public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider, IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider @@ -83,8 +52,6 @@ public class ChannelState extends Module implements ICallEventProvider, public static final long RESET_TIMEOUT_DELAY = 2000; private State mState = State.IDLE; - private long mTrafficChannelTimeout = - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS; private Listener mCallEventListener; private Listener mChangedAttributeListener; @@ -97,12 +64,15 @@ public class ChannelState extends Module implements ICallEventProvider, private boolean mSquelchLocked = false; private boolean mSelected = false; + private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY; + private long mTrafficChannelFadeTimeout = + DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; private long mFadeTimeout; - private long mResetTimeout; + private long mEndTimeout; private StateMonitor mStateMonitor = new StateMonitor(); private AtomicBoolean mMonitoring = new AtomicBoolean(); - private ScheduledFuture mRunningMonitor; + private ScheduledFuture mStateMonitorFuture; private ThreadPoolManager mThreadPoolManager; private ChannelType mChannelType; @@ -110,32 +80,97 @@ public class ChannelState extends Module implements ICallEventProvider, private TrafficChannelStatusListener mTrafficChannelStatusListener; private CallEvent mTrafficChannelCallEvent; + /** + * Channel state tracks the overall state of all processing modules and decoders + * configured for the channel and provides squelch control and decoder state + * reset events. + * + * Uses a state enumeration that defines allowable channel state transitions in + * order to track a call or data decode event from start to finish. Uses a + * timer to monitor for inactivity and to provide a FADE period that indicates + * to the user that the activity has stopped while continuing to provide details + * about the call, before the state is reset to IDLE. + * + * Since this class is multi-threaded between decoder events and the internal + * timer that monitors for inactivity, this class uses the mState object for + * thread synchronization. + * + * State Descriptions: + * IDLE Normal state. No voice or data call activity + * CALL/DATA/ENCRYPTED/CONTROL + * Decoding states. + * FADE The phase after a voice or data call when either an explicit + * call end has been received, or when no new signalling updates + * have been received, and the fade timer has expired. This phase + * allows for gui updates to signal to the user that the call is + * ended, while continuing to display the call details for the user + * TEARDOWN Indicates a traffic channel that will be torn down for reuse. + */ public ChannelState( ThreadPoolManager manager, ChannelType channelType ) { mThreadPoolManager = manager; mChannelType = channelType; } + /** + * Resets this channel state and prepares it for reuse. + */ @Override public void reset() { + broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); + broadcast( new MetadataReset() ); + + mState = State.IDLE; + broadcast( ChangedAttribute.CHANNEL_STATE ); } @Override public void start() { + //Start a monitoring task to check for FADE and END event timeouts + if( mMonitoring.compareAndSet( false, true ) ) + { + if( mStateMonitorFuture == null && mStateMonitor != null ) + { + try + { + mStateMonitorFuture = mThreadPoolManager.scheduleFixedRate( + ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); + } + catch( RejectedExecutionException ree ) + { + mLog.error( "state monitor scheduled task rejected", ree ); + mMonitoring.set( false ); + } + } + } } @Override public void stop() { mSquelchLocked = false; + + //Shutdown the state montoring task + if( mMonitoring.compareAndSet( true, false ) ) + { + if( mStateMonitorFuture != null ) + { + boolean success = mThreadPoolManager.cancel( mStateMonitorFuture ); + + if( !success ) + { + mLog.error("Couldn't stop monitoring scheduled future" ); + } + } + + mStateMonitorFuture = null; + } } public void dispose() { - stopMonitor(); - mCallEventListener = null; mChangedAttributeListener = null; mDecoderStateListener = null; @@ -144,9 +179,34 @@ public class ChannelState extends Module implements ICallEventProvider, mStateMonitor = null; } + private boolean isStandardChannel() + { + return mChannelType == ChannelType.STANDARD; + } + + private boolean isTrafficChannel() + { + return mChannelType == ChannelType.TRAFFIC; + } + + public void setStandardChannelTimeout( long milliseconds ) + { + mStandardChannelFadeTimeout = milliseconds; + + if( mChannelType == ChannelType.STANDARD ) + { + mFadeTimeout = mStandardChannelFadeTimeout; + } + } + public void setTrafficChannelTimeout( long milliseconds ) { - mTrafficChannelTimeout = milliseconds; + mTrafficChannelFadeTimeout = milliseconds; + + if( mChannelType == ChannelType.TRAFFIC ) + { + mFadeTimeout = mTrafficChannelFadeTimeout; + } } public void setSelected( boolean selected ) @@ -164,71 +224,18 @@ public class ChannelState extends Module implements ICallEventProvider, return mState; } - private boolean isIdleOrEndingState() - { - return mState == State.FADE || - mState == State.END || - mState == State.IDLE; - } - - - /** - * Starts the state monitor running when we're not in IDLE state. Calls to - * start an already started monitor are ignored. - */ - private void startMonitor() - { - if( mMonitoring.compareAndSet( false, true ) ) - { - if( mRunningMonitor == null && mStateMonitor != null ) - { - mRunningMonitor = mThreadPoolManager.scheduleFixedRate( - ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); - } - else - { - throw new RuntimeException( "Channel state monitor's scheduled " - + "future pointer was not null" ); - } - } - } - - /** - * Stops the state monitor when we're in an IDLE state. Calls to stop an - * already stopped monitor are ignored. - */ - private void stopMonitor() - { - if( mMonitoring.compareAndSet( true, false ) ) - { - if( mRunningMonitor != null ) - { - mThreadPoolManager.cancel( mRunningMonitor ); - } - - mRunningMonitor = null; - } - } - /** * Updates the fade timeout threshold to the current time plus delay */ private void updateFadeTimeout() { - /* Standard channel uses the FADE_TIMEOUT_DELAY and traffic channels - * use the TRAFFIC_FADE_TIMEOUT_DELAY. Some traffic channels (e.g. MPT) - * don't have signaling on the traffic channel that can be used to - * update the fade timeout, so we set the timeout value larger to ensure - * the channel doesn't fade before the end of the call. */ - long current = System.currentTimeMillis(); - - long timeout = current + - ( mChannelType == ChannelType.STANDARD ? FADE_TIMEOUT_DELAY : - mTrafficChannelTimeout ); - - if( timeout > mFadeTimeout ) + if( isTrafficChannel() ) { - mFadeTimeout = timeout; + mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout; + } + else + { + mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout; } } @@ -237,13 +244,13 @@ public class ChannelState extends Module implements ICallEventProvider, */ private void updateResetTimeout() { - if( mChannelType == ChannelType.TRAFFIC ) + if( isTrafficChannel() ) { - mResetTimeout = System.currentTimeMillis(); + mEndTimeout = System.currentTimeMillis(); } else { - mResetTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY; + mEndTimeout = System.currentTimeMillis() + RESET_TIMEOUT_DELAY; } } @@ -285,120 +292,120 @@ public class ChannelState extends Module implements ICallEventProvider, */ protected void setState( State state ) { - if( mState == state ) + synchronized( mState ) { - switch( state ) + if( state == mState ) { - case CALL: - case CONTROL: - case DATA: - case ENCRYPTED: - updateFadeTimeout(); - break; - default: - break; - } - } - else if( mState.canChangeTo( state ) ) - { - switch( state ) - { - case CONTROL: - //Don't allow traffic channels to be control channels, - //otherwise they won't get call tear down - if( mChannelType != ChannelType.TRAFFIC ) - { - broadcast( SquelchState.SQUELCH ); - updateFadeTimeout(); - startMonitor(); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - } - break; - case DATA: - case ENCRYPTED: - broadcast( SquelchState.SQUELCH ); - updateFadeTimeout(); - startMonitor(); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - case CALL: - broadcast( SquelchState.UNSQUELCH ); - updateFadeTimeout(); - startMonitor(); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - case FADE: - broadcast( SquelchState.SQUELCH ); - updateResetTimeout(); - startMonitor(); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - case END: - broadcast( SquelchState.SQUELCH ); - broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); - broadcast( new MetadataReset() ); - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - case IDLE: - broadcast( SquelchState.SQUELCH ); - stopMonitor(); - - if( mChannelType == ChannelType.TRAFFIC ) - { - if( mTrafficChannelStatusListener != null ) - { - mTrafficChannelStatusListener.callEnd(); - mTrafficChannelStatusListener = null; - } - - if( mTrafficChannelCallEvent != null ) - { - mTrafficChannelCallEvent.end(); - - broadcast( mTrafficChannelCallEvent ); - mTrafficChannelCallEvent = null; - } - } - - mState = state; - broadcast( ChangedAttribute.CHANNEL_STATE ); - break; - default: - break; - } - } - } - - public class StateMonitor implements Runnable - { - @Override - public void run() - { - if( getState() == State.FADE ) - { - if( mResetTimeout <= System.currentTimeMillis() ) + if( State.CALL_STATES.contains( state ) ) { - setState( State.END ); + updateFadeTimeout(); } } - else if( getState() == State.END ) + else if( state != mState && mState.canChangeTo( state ) ) { - setState( State.IDLE ); + switch( state ) + { + case CONTROL: + //Don't allow traffic channels to be control channels, + //otherwise they can't transition to call tear down + if( isStandardChannel() ) + { + broadcast( SquelchState.SQUELCH ); + updateFadeTimeout(); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + } + break; + case DATA: + case ENCRYPTED: + broadcast( SquelchState.SQUELCH ); + updateFadeTimeout(); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + case CALL: + broadcast( SquelchState.UNSQUELCH ); + updateFadeTimeout(); + mState = state; + broadcast( ChangedAttribute.CHANNEL_STATE ); + break; + case FADE: + processFadeState(); + break; + case IDLE: + processIdleState(); + break; + case TEARDOWN: + processTeardownState(); + break; + case RESET: + mState = State.IDLE; + break; + default: + break; + } } else { - if( mFadeTimeout <= System.currentTimeMillis() ) - { - setState( State.FADE ); - } +// mLog.debug( "Can't change from [" + mState + "] to [" + state + "]" ); } } } + + /** + * Sets the state and processes related actions + */ + private void processFadeState() + { + updateResetTimeout(); + mState = State.FADE; + + broadcast( SquelchState.SQUELCH ); + broadcast( ChangedAttribute.CHANNEL_STATE ); + } + + private void processIdleState() + { + broadcast( SquelchState.SQUELCH ); + + if( mState == State.FADE ) + { + broadcast( new DecoderStateEvent( this, Event.RESET, State.IDLE ) ); + broadcast( new MetadataReset() ); + } + + mState = State.IDLE; + + broadcast( ChangedAttribute.CHANNEL_STATE ); + } + + private void processTeardownState() + { + broadcast( SquelchState.SQUELCH ); + + mState = State.TEARDOWN; + + broadcast( ChangedAttribute.CHANNEL_STATE ); + + if( mTrafficChannelCallEvent != null ) + { + mTrafficChannelCallEvent.end(); + + broadcast( mTrafficChannelCallEvent ); + mTrafficChannelCallEvent = null; + } + + if( mTrafficChannelStatusListener != null ) + { + mTrafficChannelStatusListener.callEnd(); + mTrafficChannelStatusListener = null; + } + else + { + mLog.error( "Traffic channel status listener was null - couldn't " + + "teardown the channel"); + } + } /** * Broadcasts the call event to the registered listener @@ -590,35 +597,95 @@ public class ChannelState extends Module implements ICallEventProvider, broadcast( SquelchState.UNSQUELCH ); mSquelchLocked = true; break; + case CHANGE_CALL_TIMEOUT: + if( event instanceof ChangeChannelTimeoutEvent ) + { + ChangeChannelTimeoutEvent timeout = (ChangeChannelTimeoutEvent)event; + + if( timeout.getChannelType() == ChannelType.STANDARD ) + { + setStandardChannelTimeout( timeout.getCallTimeout() ); + } + else + { + setTrafficChannelTimeout( timeout.getCallTimeout() ); + } + } case CONTINUATION: - if( isIdleOrEndingState() ) - { - setState( event.getState() ); - } - else - { - updateFadeTimeout(); - } - break; case DECODE: - if( getState() == State.IDLE ) + case START: + if( State.CALL_STATES.contains( event.getState() ) ) { setState( event.getState() ); } break; case END: - setState( State.FADE ); + if( isTrafficChannel() ) + { + setState( State.TEARDOWN ); + } + else + { + setState( State.FADE ); + } break; case RESET: /* Channel State does not respond to reset events */ break; - case START: - setState( event.getState() ); - break; default: break; } } } } -} + + /** + * Monitors decoder state events to automatically transition the channel + * state to IDLE (standard channel) or to TEARDOWN (traffic channel) when + * decoding stops or the monitored channel returns to a no signal state. + * + * Provides a FADE transition state to allow for momentary decoding dropouts + * and to allow the user access to call details for a fade period upon + * call end. + */ + public class StateMonitor implements Runnable + { + @Override + public void run() + { + try + { + synchronized( mState ) + { + if( State.CALL_STATES.contains( mState ) && + mFadeTimeout <= System.currentTimeMillis() ) + { + mState = State.FADE; + processFadeState(); + } + else if( mState == State.FADE && + mEndTimeout <= System.currentTimeMillis() ) + { + if( isTrafficChannel() ) + { + processTeardownState(); + } + else + { + processIdleState(); + } + } + } + } + catch( Exception e ) + { + mLog.error( "Exception thrown while state monitor is running " + + "- state [" + getState() + + "] current [" + System.currentTimeMillis() + + "] mResetTimeout [" + mEndTimeout + + "] mFadeTimeout [" + mFadeTimeout + + "]", e ); + } + } + } +} \ No newline at end of file diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelStatePanel.java index e133989f..9b9dd7cb 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelStatePanel.java @@ -254,38 +254,8 @@ public class ChannelStatePanel extends JPanel Paint p = null; - final Channel channel = mChannel; - - final ChannelState channelState = ( channel == null ? null : channel.getChannelState() ); - - if( channelState != null ) + switch( mChannelState.getState() ) { -<<<<<<< Upstream, based on origin/master - switch( channelState.getState() ) - { - case CALL: - p = getGradient( mColorTopCall, mColorMiddleCall ); - break; - case CONTROL: - p = getGradient( mColorTopControl, mColorMiddleControl ); - break; - case DATA: - p = getGradient( mColorTopData, mColorMiddleData ); - break; - case FADE: - p = getGradient( mColorTopFade, mColorMiddleFade ); - break; - case IDLE: - default: - p = getGradient( mColorTopIdle, mColorMiddleIdle ); - break; - } - } - - if( p == null ) - { - p = getGradient( mColorTopIdle, mColorMiddleIdle ); -======= case CALL: p = getGradient( mColorTopCall, mColorMiddleCall ); break; @@ -298,13 +268,11 @@ public class ChannelStatePanel extends JPanel case FADE: p = getGradient( mColorTopFade, mColorMiddleFade ); break; - case END: case IDLE: default: p = getGradient( mColorTopIdle, mColorMiddleIdle ); break; ->>>>>>> 0e01381 Updates. - } + } g2.setPaint( p ); g2.fillRect( 0, 0, getWidth(), getHeight() ); diff --git a/src/module/decode/state/DecoderStateEvent.java b/src/module/decode/state/DecoderStateEvent.java index 19623bbc..a4ebd68f 100644 --- a/src/module/decode/state/DecoderStateEvent.java +++ b/src/module/decode/state/DecoderStateEvent.java @@ -56,6 +56,7 @@ public class DecoderStateEvent public enum Event { ALWAYS_UNSQUELCH, + CHANGE_CALL_TIMEOUT, CONTINUATION, DECODE, END, @@ -64,4 +65,4 @@ public class DecoderStateEvent START, TRAFFIC_CHANNEL_ALLOCATION; } -} +} \ No newline at end of file diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java index 2820429d..1f1bc79b 100644 --- a/src/module/decode/state/State.java +++ b/src/module/decode/state/State.java @@ -1,5 +1,27 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package module.decode.state; +import java.util.EnumSet; + +/** + * Details the set of states for a channel and the allowable transition states + */ public enum State { IDLE( "IDLE" ) @@ -7,7 +29,8 @@ public enum State @Override public boolean canChangeTo( State state ) { - return true; + return state != TEARDOWN && + state != RESET; } }, CALL( "CALL" ) @@ -15,11 +38,11 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state == CALL || - state == CONTROL || + return state == CONTROL || state == DATA || state == ENCRYPTED || - state == FADE; + state == FADE || + state == TEARDOWN; } }, DATA( "DATA" ) @@ -29,9 +52,9 @@ public enum State { return state == CALL || state == CONTROL || - state == DATA || - state == State.ENCRYPTED || - state == FADE; + state == ENCRYPTED || + state == FADE || + state == TEARDOWN; } }, ENCRYPTED( "ENCRYPTED" ) @@ -39,7 +62,7 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state == FADE; + return state == FADE || state == TEARDOWN; } }, CONTROL( "CONTROL" ) @@ -47,7 +70,8 @@ public enum State @Override public boolean canChangeTo( State state ) { - return true; + return state == IDLE || + state == FADE; } }, FADE( "FADE" ) @@ -55,20 +79,36 @@ public enum State @Override public boolean canChangeTo( State state ) { - return state != FADE; //All states except fade allowed - } + return state != FADE && + state != RESET; + } }, - END( "END" ) + RESET( "RESET" ) { - @Override - public boolean canChangeTo( State state ) - { - return state != FADE; //Fade is only disallowed state - } - }; + @Override + public boolean canChangeTo(State state) + { + return state == IDLE; + } + }, + + TEARDOWN( "TEARDOWN" ) + { + @Override + public boolean canChangeTo(State state) + { + return state == RESET; + } + }; private String mDisplayValue; + public static final EnumSet CALL_STATES = + EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); + + public static final EnumSet IDLE_STATES = + EnumSet.of( IDLE, FADE ); + private State( String displayValue ) { mDisplayValue = displayValue; @@ -80,4 +120,4 @@ public enum State { return mDisplayValue; } -} +} \ No newline at end of file diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index ae97cfbb..167f5180 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -66,7 +66,7 @@ public class PlaylistManager implements ChannelEventListener private boolean mPlaylistLoading = false; /** - * Playlist manager - manages all channel configurationc, channel maps, and + * Playlist manager - manages all channel configurations, channel maps, and * alias lists and handles loading or persisting to a playlist.xml file * * Monitors playlist changes to automatically save configuration changes @@ -103,8 +103,11 @@ public class PlaylistManager implements ChannelEventListener mPlaylistLoading = true; + mLog.debug( "Transferring aliases to model" ); mAliasModel.addAliases( mPlaylist.getAliases() ); + mLog.debug( "Transferring channels to model" ); mChannelModel.addChannels( mPlaylist.getChannels() ); + mLog.debug( "Transfer to models is complete" ); mPlaylistLoading = false; } @@ -126,48 +129,6 @@ public class PlaylistManager implements ChannelEventListener props.get( "playlist.currentfilename", defaultPlaylistFile ); load( playlistFolder.resolve( playlistFile ) ); - - transferPlaylistToModels(); - } - - /** - * Channel event listener method. Monitors channel events for events that - * indicate that the playlist has changed and queues automatic playlist - * saving. - */ - @Override - public void channelChanged( ChannelEvent event ) - { - //Only save playlist for changes to standard channels (not traffic) - if( event.getChannel().getChannelType() == ChannelType.STANDARD ) - { - switch( event.getEvent() ) - { - case NOTIFICATION_ADD: - case NOTIFICATION_CONFIGURATION_CHANGE: - case NOTIFICATION_DELETE: - case NOTIFICATION_PROCESSING_START: - case NOTIFICATION_PROCESSING_STOP: - schedulePlaylistSave(); - break; - case NOTIFICATION_ENABLE_REJECTED: - case NOTIFICATION_SELECTION_CHANGE: - case NOTIFICATION_STATE_RESET: - case REQUEST_DELETE: - case REQUEST_DESELECT: - case REQUEST_DISABLE: - case REQUEST_ENABLE: - case REQUEST_SELECT: - //Do nothing for these event types - break; - default: - //When a new event enum entry is added and received, throw an - //exception here to ensure developer adds support for the - //use case - throw new IllegalArgumentException( "Unrecognized Channel " - + "Event [" + event.getEvent().name() + "]" ); - } - } } /** @@ -426,7 +387,6 @@ public class PlaylistManager implements ChannelEventListener saveRequired = true; } - //Check for and convert from legacy play list format if( mPlaylist.hasSystemList() || mPlaylist.hasAliasDirectory() ) { diff --git a/src/source/mixer/MixerChannelConfiguration.java b/src/source/mixer/MixerChannelConfiguration.java index 0be3b358..b8520bcc 100644 --- a/src/source/mixer/MixerChannelConfiguration.java +++ b/src/source/mixer/MixerChannelConfiguration.java @@ -23,6 +23,14 @@ public class MixerChannelConfiguration return mMixerChannel; } + public boolean matches( String mixer, String channels ) + { + return mixer != null && + channels != null && + mMixer.getMixerInfo().getName().contentEquals( mixer ) && + mMixerChannel.name().contentEquals( channels ); + } + public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 44682e7a..faf36961 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -28,7 +28,6 @@ import source.SourceException; import source.tuner.frequency.FrequencyController; import source.tuner.frequency.FrequencyController.Tunable; import source.tuner.frequency.IFrequencyChangeProcessor; -import source.tuner.frequency.IFrequencyChangeListener; import controller.ThreadPoolManager; public abstract class TunerController implements Tunable @@ -37,11 +36,7 @@ public abstract class TunerController implements Tunable LoggerFactory.getLogger( TunerController.class ); /* List of currently tuned channels being served to demod channels */ -<<<<<<< Upstream, based on origin/master private SortedSet mTunedChannels = new ConcurrentSkipListSet<>(); -======= - protected ArrayList mTunedChannels = new ArrayList<>(); ->>>>>>> 20e3bee Work in progress. -Started work on new channel editor channel viewer/controller -Removed a number of unused buffer package classes -Moved automatic frequency control to be a module and removed special handling -Added IUnfilteredRealBufferListener & Provider interfaces -Completely removed the ResourceManager class protected FrequencyController mFrequencyController; private int mMiddleUnusable; private double mUsableBandwidthPercentage; From 2c6e35c73973a8b699654ce65ac75de5e0425058 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 07:10:41 -0500 Subject: [PATCH 16/74] Work in progress. -Removed channel collection panel since panels will only have 1 channel now -Resolved issue with channel list and channel selection events -Fixed some of the classes from the rebase to master branch --- src/alias/AliasModel.java | 37 +---- src/controller/ControllerPanel.java | 3 +- src/controller/channel/Channel.java | 11 +- src/controller/channel/ChannelController.java | 52 ++++++ src/controller/channel/ChannelModel.java | 44 +---- .../channel/ChannelProcessingManager.java | 5 +- src/gui/SDRTrunk.java | 31 ++-- src/module/ProcessingChain.java | 11 +- .../decode/event/MessageActivityModel.java | 10 +- .../decode/event/MessageActivityPanel.java | 2 +- src/module/decode/p25/P25MessageFramer.java | 3 +- src/module/decode/p25/P25_LSMDecoder.java | 62 +++---- .../decode/p25/reference/DataUnitID.java | 6 +- .../state/ChangeChannelTimeoutEvent.java | 29 ++++ .../decode/state/ChannelCollectionPanel.java | 154 ------------------ src/module/decode/state/ChannelList.java | 42 +++-- ...annelStatePanel.java => ChannelPanel.java} | 57 ++++--- src/playlist/PlaylistManager.java | 7 - .../ComplexBufferToStreamConverter.java | 8 +- 19 files changed, 244 insertions(+), 330 deletions(-) create mode 100644 src/module/decode/state/ChangeChannelTimeoutEvent.java delete mode 100644 src/module/decode/state/ChannelCollectionPanel.java rename src/module/decode/state/{ChannelStatePanel.java => ChannelPanel.java} (92%) diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index 3206a57e..b1168f79 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -24,20 +24,22 @@ import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; -import javax.swing.table.TableModel; +import javax.swing.table.AbstractTableModel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import alias.AliasEvent.Event; import sample.Broadcaster; import sample.Listener; +import alias.AliasEvent.Event; /** * Alias Model */ -public class AliasModel implements TableModel +public class AliasModel extends AbstractTableModel { + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( AliasModel.class ); public static final int COLUMN_LIST = 0; @@ -47,7 +49,6 @@ public class AliasModel implements TableModel public static final int COLUMN_COLOR = 4; private List mAliases = new ArrayList<>(); - private List mTableModelListeners = new CopyOnWriteArrayList<>(); private Broadcaster mAliasEventBroadcaster = new Broadcaster<>(); public AliasModel() @@ -141,8 +142,7 @@ public class AliasModel implements TableModel int index = mAliases.size() - 1; - broadcast( new TableModelEvent( this, index, index, - TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT ) ); + fireTableRowsInserted( index, index ); broadcast( new AliasEvent( alias, Event.ADD ) ); @@ -163,8 +163,7 @@ public class AliasModel implements TableModel mAliases.remove( alias ); - broadcast( new TableModelEvent( this, index, index, - TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); + fireTableRowsDeleted( index, index ); broadcast( new AliasEvent( alias, Event.DELETE ) ); } @@ -247,26 +246,6 @@ public class AliasModel implements TableModel throw new IllegalArgumentException( "Not yet implemented" ); } - private void broadcast( TableModelEvent event ) - { - for( TableModelListener listener: mTableModelListeners ) - { - listener.tableChanged( event ); - } - } - - @Override - public void addTableModelListener( TableModelListener listener ) - { - mTableModelListeners.add( listener ); - } - - @Override - public void removeTableModelListener( TableModelListener listener ) - { - mTableModelListeners.remove( listener ); - } - public void broadcast( AliasEvent event ) { mAliasEventBroadcaster.broadcast( event ); @@ -275,7 +254,7 @@ public class AliasModel implements TableModel { int index = mAliases.indexOf( event.getAlias() ); - broadcast( new TableModelEvent( this, index ) ); + fireTableRowsUpdated( index, index ); } } diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index ef7d8619..8c58d145 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -99,8 +99,7 @@ public class ControllerPanel extends JPanel mMessageActivityPanel = new MessageActivityPanel( channelProcessingManager ); - mCallEventPanel = new CallEventPanel( mSettingsManager, - channelProcessingManager ); + mCallEventPanel = new CallEventPanel( mSettingsManager, channelProcessingManager ); mChannelSpectrumPanel = new ChannelSpectrumPanel( mSettingsManager, channelProcessingManager ); diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index be2bf97f..f9aca2c6 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -179,7 +179,16 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor return mSelected; } - public void setSelected( boolean selected ) + /** + * Sets selection status of the channel. + * + * NOTE: this method is package private so that only the ChannelSelectionManager + * can toggle the selection state. Use the channel model to broadcast a + * request select/deselect channel event. + * + * @param selected + */ + void setSelected( boolean selected ) { mSelected = selected; } diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java index 88b9fe9e..498fae64 100644 --- a/src/controller/channel/ChannelController.java +++ b/src/controller/channel/ChannelController.java @@ -1,5 +1,7 @@ package controller.channel; +import java.awt.Color; +import java.awt.Component; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -12,6 +14,7 @@ import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; import net.coderazzi.filters.gui.AutoChoices; import net.coderazzi.filters.gui.TableFilterHeader; @@ -64,6 +67,7 @@ public class ChannelController extends JPanel //System Configuration View and Editor mChannelTable = new JTable( mChannelModel ); + mChannelTable.setDefaultRenderer( String.class, new ChannelTableCellRenderer() ); mChannelTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); mChannelTable.getSelectionModel().addListSelectionListener( this ); mChannelTable.setAutoCreateRowSorter( true ); @@ -216,4 +220,52 @@ public class ChannelController extends JPanel break; } } + + public class ChannelTableCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + Component component = super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column ); + + ChannelModel model = (ChannelModel)table.getModel(); + + int index = table.convertRowIndexToModel( row ); + + Channel channel = model.getChannelAtIndex( index ); + + boolean enabled = channel.getEnabled(); + + if( isSelected ) + { + if( enabled ) + { + component.setBackground( Color.YELLOW ); + } + else + { + component.setBackground( table.getSelectionBackground() ); + } + } + else + { + if( enabled ) + { + component.setBackground( Color.GREEN ); + } + else + { + component.setBackground( table.getBackground() ); + } + } + + return component; + } + + } } diff --git a/src/controller/channel/ChannelModel.java b/src/controller/channel/ChannelModel.java index 0ec88eb9..25240ec1 100644 --- a/src/controller/channel/ChannelModel.java +++ b/src/controller/channel/ChannelModel.java @@ -22,9 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; -import javax.swing.table.TableModel; +import javax.swing.table.AbstractTableModel; import module.decode.DecoderType; @@ -37,20 +35,18 @@ import controller.channel.ChannelEvent.Event; /** * Channel Model */ -public class ChannelModel implements IChannelEventBroadcaster, TableModel +public class ChannelModel extends AbstractTableModel implements IChannelEventBroadcaster { + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( ChannelModel.class ); - private ChannelSelectionManager mChannelSelectionManager; - private List mChannels = new ArrayList<>(); - private List mTrafficChannels = new ArrayList<>(); + private List mChannels = new CopyOnWriteArrayList<>(); + private List mTrafficChannels = new CopyOnWriteArrayList<>(); private List mListeners = new CopyOnWriteArrayList<>(); - private List mTableModelListeners = new CopyOnWriteArrayList<>(); public ChannelModel() { - mChannelSelectionManager = new ChannelSelectionManager( this ); - addListener( mChannelSelectionManager ); } /** @@ -138,7 +134,7 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel case NOTIFICATION_PROCESSING_STOP: case NOTIFICATION_SELECTION_CHANGE: int index = mChannels.indexOf( event.getChannel() ); - broadcast( new TableModelEvent( this, index ) ); + fireTableRowsUpdated( index, index ); break; default: break; @@ -177,8 +173,7 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel index = mChannels.size() - 1; - broadcast( new TableModelEvent( this, index, index, - TableModelEvent.ALL_COLUMNS, TableModelEvent.INSERT ) ); + fireTableRowsInserted( index, index ); break; case TRAFFIC: mTrafficChannels.add( channel ); @@ -214,8 +209,7 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel if( index >= 0 ) { - broadcast( new TableModelEvent( this, index, index, - TableModelEvent.ALL_COLUMNS, TableModelEvent.DELETE ) ); + fireTableRowsDeleted( index, index ); } break; case TRAFFIC: @@ -348,26 +342,6 @@ public class ChannelModel implements IChannelEventBroadcaster, TableModel throw new IllegalArgumentException( "Not yet implemented" ); } - private void broadcast( TableModelEvent event ) - { - for( TableModelListener listener: mTableModelListeners ) - { - listener.tableChanged( event ); - } - } - - @Override - public void addTableModelListener( TableModelListener listener ) - { - mTableModelListeners.add( listener ); - } - - @Override - public void removeTableModelListener( TableModelListener listener ) - { - mTableModelListeners.remove( listener ); - } - public void createChannel( DecoderType decoderType, long frequency ) { throw new UnsupportedOperationException( "Not yet implemented" ); diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index 610c4437..d1a5f423 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -77,8 +77,6 @@ public class ChannelProcessingManager implements ChannelEventListener mRecorderManager = recorderManager; mSourceManager = sourceManager; mThreadPoolManager = threadPoolManager; - - mChannelModel.addListener( this ); } /** @@ -184,7 +182,7 @@ public class ChannelProcessingManager implements ChannelEventListener return; } - + if( processingChain == null ) { processingChain = new ProcessingChain( mThreadPoolManager, @@ -258,6 +256,7 @@ public class ChannelProcessingManager implements ChannelEventListener } processingChain.setSource( source ); + processingChain.start(); channel.setEnabled( true ); diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index ef21d641..577c6593 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -119,10 +119,10 @@ public class SDRTrunk SourceManager sourceManager = new SourceManager( mSettingsManager, threadPoolManager ); - AliasModel aliasModel = new AliasModel(); - ChannelModel channelModel = new ChannelModel(); + AliasModel aliasModel = new AliasModel(); + PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, aliasModel, channelModel ); @@ -137,7 +137,11 @@ public class SDRTrunk channelProcessingManager.addAudioPacketListener( recorderManager ); - AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); + ChannelSelectionManager channelSelectionManager = + new ChannelSelectionManager( channelModel ); + channelModel.addListener( channelSelectionManager ); + + AliasActionManager aliasActionManager = new AliasActionManager( threadPoolManager ); channelProcessingManager.addMessageListener( aliasActionManager ); AudioManager audioManager = new AudioManager( threadPoolManager, sourceManager.getMixerManager() ); @@ -145,16 +149,6 @@ public class SDRTrunk MapService mapService = new MapService( mSettingsManager ); channelProcessingManager.addMessageListener( mapService ); - - ChannelSelectionManager channelSelectionManager = - new ChannelSelectionManager( channelModel ); - channelModel.addListener( channelSelectionManager ); - -//TODO: move this to the end once we add the alias model, otherwise the -//configuration tree won't load correctly - - //Initialize the playlist manager to load the saved playlist - playlistManager.init(); ConfigurationControllerModel configurationControllerModel = new ConfigurationControllerModel( channelModel, channelProcessingManager, @@ -169,11 +163,16 @@ public class SDRTrunk mTitle = getTitle(); - //Initialize the GUI + //TODO: move this to the end once we add the alias model, otherwise the + //configuration tree won't load correctly + //Initialize the playlist manager to load the saved playlist + playlistManager.init(); + + mLog.info( "starting main application gui" ); + + //Initialize the GUI initGUI(); - mLog.info( "starting main application gui" ); - if( mLogChannelAndMemoryUsage ) { Runnable cml = new ChannelMemoryLogger(); diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index e48e644e..af501560 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -475,13 +475,15 @@ public class ProcessingChain implements IChannelEventListener } /* If this is a tuner source, broadcast the frequency to all - * of the decoder state's */ + * of the decoder state's and start the samples flowing */ if( mSource instanceof TunerChannelSource ) { + TunerChannelSource tcs = (TunerChannelSource)mSource; + try { - long frequency = ((TunerChannelSource)mSource).getFrequency(); - + long frequency = tcs.getFrequency(); + mDecoderStateEventBroadcaster.broadcast( new DecoderStateEvent( this, Event.SOURCE_FREQUENCY, State.IDLE, frequency ) ); @@ -490,6 +492,8 @@ public class ProcessingChain implements IChannelEventListener { mLog.error( "Error getting frequency from tuner channel source", e ); } + + tcs.start(); } } else @@ -603,6 +607,7 @@ public class ProcessingChain implements IChannelEventListener */ public void addMessageListener( Listener listener ) { + mLog.debug( "Registering Message Listener:" + listener.getClass() ); mMessageBroadcaster.addListener( listener ); } diff --git a/src/module/decode/event/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java index 65850196..ddcf17a5 100644 --- a/src/module/decode/event/MessageActivityModel.java +++ b/src/module/decode/event/MessageActivityModel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -24,13 +24,19 @@ import java.util.LinkedList; import javax.swing.table.AbstractTableModel; import message.Message; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sample.Listener; import filter.FilterSet; public class MessageActivityModel extends AbstractTableModel implements Listener { - private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( MessageActivityModel.class ); + + private static final long serialVersionUID = 1L; private static final int sTIME = 0; private static final int sPROTOCOL = 1; private static final int sERROR_STATUS = 2; diff --git a/src/module/decode/event/MessageActivityPanel.java b/src/module/decode/event/MessageActivityPanel.java index 6cc4ac71..0ef6a5ee 100644 --- a/src/module/decode/event/MessageActivityPanel.java +++ b/src/module/decode/event/MessageActivityPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 diff --git a/src/module/decode/p25/P25MessageFramer.java b/src/module/decode/p25/P25MessageFramer.java index 84ed4154..39d9bb80 100644 --- a/src/module/decode/p25/P25MessageFramer.java +++ b/src/module/decode/p25/P25MessageFramer.java @@ -131,7 +131,7 @@ public class P25MessageFramer implements Listener if( demodulator != null ) { /* For CQPSK, we include 3 additional sync detectors to watch for and - * correction +/-90 and 180 degree costas loop phase lock errors */ + * correct +/-90 and 180 degree costas loop phase lock errors */ mMatcher.add( new CostasPhaseErrorDetector( FrameSync.P25_PHASE1_ERROR_90_CCW, demodulator, PHASE_CORRECTION_90_DEGREES ) ); @@ -291,6 +291,7 @@ public class P25MessageFramer implements Listener else { mComplete = true; + dispatch( new P25Message( mMessage.copy(), mDUID, mAliasList ) ); } } else diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index 0e72cba4..7614ac24 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -49,8 +49,8 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener /* Instrumentation Taps */ private static final String INSTRUMENT_BASEBAND_FILTER_OUTPUT = "Tap Point: Baseband Filter Output"; private static final String INSTRUMENT_AGC_OUTPUT = "Tap Point: AGC Output"; - private static final String INSTRUMENT_QPSK_DEMODULATOR_OUTPUT = "Tap Point: CQPSK Demodulator Output"; - private static final String INSTRUMENT_CQPSK_SLICER_OUTPUT = "Tap Point: CQPSK Slicer Output"; + private static final String INSTRUMENT_LSM_DEMODULATOR_OUTPUT = "Tap Point: LSM Demodulator Output"; + private static final String INSTRUMENT_QPSK_SLICER_OUTPUT = "Tap Point: QPSK Slicer Output"; private List mAvailableTaps; @@ -59,8 +59,8 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener new ComplexBufferToStreamConverter(); private ComplexFeedForwardGainControl mAGC = new ComplexFeedForwardGainControl( 32 ); - private LSMDemodulator mCQPSKDemodulator = new LSMDemodulator(); - private QPSKPolarSlicer mCQPSKSlicer = new QPSKPolarSlicer(); + private LSMDemodulator mLSMDemodulator = new LSMDemodulator(); + private QPSKPolarSlicer mQPSKSlicer = new QPSKPolarSlicer(); private P25MessageFramer mMessageFramer; public P25_LSMDecoder( AliasList aliasList ) @@ -74,12 +74,12 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener mStreamConverter.setListener( mAGC ); - mAGC.setListener( mCQPSKDemodulator ); + mAGC.setListener( mLSMDemodulator ); - mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + mLSMDemodulator.setSymbolListener( mQPSKSlicer ); - mMessageFramer = new P25MessageFramer( aliasList, mCQPSKDemodulator ); - mCQPSKSlicer.addListener( mMessageFramer ); + mMessageFramer = new P25MessageFramer( aliasList, mLSMDemodulator ); + mQPSKSlicer.addListener( mMessageFramer ); mMessageFramer.setListener( getMessageProcessor() ); } @@ -97,11 +97,11 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener mAGC.dispose(); mAGC = null; - mCQPSKDemodulator.dispose(); - mCQPSKDemodulator = null; + mLSMDemodulator.dispose(); + mLSMDemodulator = null; - mCQPSKSlicer.dispose(); - mCQPSKSlicer = null; + mQPSKSlicer.dispose(); + mQPSKSlicer = null; mMessageFramer.dispose(); mMessageFramer = null; @@ -132,14 +132,14 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener group.add( new ComplexTap( INSTRUMENT_BASEBAND_FILTER_OUTPUT, 0, 1.0f ) ); group.add( new ComplexTap( INSTRUMENT_AGC_OUTPUT, 0, 1.0f ) ); - group.add( new QPSKTap( INSTRUMENT_QPSK_DEMODULATOR_OUTPUT, 0, 1.0f ) ); - group.add( new DibitTap( INSTRUMENT_CQPSK_SLICER_OUTPUT, 0, 0.1f ) ); + group.add( new QPSKTap( INSTRUMENT_LSM_DEMODULATOR_OUTPUT, 0, 1.0f ) ); + group.add( new DibitTap( INSTRUMENT_QPSK_SLICER_OUTPUT, 0, 0.1f ) ); mAvailableTaps.add( group ); - if( mCQPSKDemodulator != null ) + if( mLSMDemodulator != null ) { - mAvailableTaps.addAll( mCQPSKDemodulator.getTapGroups() ); + mAvailableTaps.addAll( mLSMDemodulator.getTapGroups() ); } } @@ -152,9 +152,9 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener @Override public void registerTap( Tap tap ) { - if( mCQPSKDemodulator != null ) + if( mLSMDemodulator != null ) { - mCQPSKDemodulator.registerTap( tap ); + mLSMDemodulator.registerTap( tap ); } switch( tap.getName() ) @@ -167,15 +167,15 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener case INSTRUMENT_AGC_OUTPUT: ComplexSampleTap agcSymbol = (ComplexSampleTap)tap; mAGC.setListener( agcSymbol ); - agcSymbol.setListener( mCQPSKDemodulator ); + agcSymbol.setListener( mLSMDemodulator ); break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: + case INSTRUMENT_LSM_DEMODULATOR_OUTPUT: QPSKTap qpsk = (QPSKTap)tap; - mCQPSKDemodulator.setSymbolListener( qpsk ); - qpsk.setListener( mCQPSKSlicer ); + mLSMDemodulator.setSymbolListener( qpsk ); + qpsk.setListener( mQPSKSlicer ); break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.addListener( (DibitTap)tap ); + case INSTRUMENT_QPSK_SLICER_OUTPUT: + mQPSKSlicer.addListener( (DibitTap)tap ); break; default: throw new IllegalArgumentException( "Unrecognized tap: " + @@ -189,9 +189,9 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener @Override public void unregisterTap( Tap tap ) { - if( mCQPSKDemodulator != null ) + if( mLSMDemodulator != null ) { - mCQPSKDemodulator.unregisterTap( tap ); + mLSMDemodulator.unregisterTap( tap ); } switch( tap.getName() ) @@ -200,13 +200,13 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener mStreamConverter.setListener( mAGC ); break; case INSTRUMENT_AGC_OUTPUT: - mAGC.setListener( mCQPSKDemodulator ); + mAGC.setListener( mLSMDemodulator ); break; - case INSTRUMENT_QPSK_DEMODULATOR_OUTPUT: - mCQPSKDemodulator.setSymbolListener( mCQPSKSlicer ); + case INSTRUMENT_LSM_DEMODULATOR_OUTPUT: + mLSMDemodulator.setSymbolListener( mQPSKSlicer ); break; - case INSTRUMENT_CQPSK_SLICER_OUTPUT: - mCQPSKSlicer.removeListener( (DibitTap)tap ); + case INSTRUMENT_QPSK_SLICER_OUTPUT: + mQPSKSlicer.removeListener( (DibitTap)tap ); break; default: throw new IllegalArgumentException( "Unrecognized tap: " + diff --git a/src/module/decode/p25/reference/DataUnitID.java b/src/module/decode/p25/reference/DataUnitID.java index c52e9af3..eba7fa42 100644 --- a/src/module/decode/p25/reference/DataUnitID.java +++ b/src/module/decode/p25/reference/DataUnitID.java @@ -14,7 +14,7 @@ public enum DataUnitID TSBK1( 7, 260, false, "TSBK1", "Trunking Signaling Block" ), TSBK2( 7, 260, false, "TSBK2", "Trunking Signaling Block" ), TSBK3( 7, 260, false, "TSBK3", "Trunking Signaling Block" ), - VSELP2( 9, 1680, false, "VSEL1", "Motorola VSELP Unit 1" ), + VSELP2( 9, 1680, false, "VSEL2", "Motorola VSELP Unit 2" ), LDU2 ( 10, 1632, true, "LDU2 ", "Logical Link Data Unit 2" ), PDU0 ( 12, 260, false, "PDU0 ", "Packet Header Data Unit" ), PDU1 ( 12, 356, false, "PDU1 ", "Packet Data Unit-Unconfirmed" ), @@ -75,8 +75,12 @@ public enum DataUnitID return TDU; case 5: return LDU1; + case 6: + return VSELP1; case 7: return TSBK1; + case 9: + return VSELP2; case 10: return LDU2; case 12: diff --git a/src/module/decode/state/ChangeChannelTimeoutEvent.java b/src/module/decode/state/ChangeChannelTimeoutEvent.java new file mode 100644 index 00000000..9119e4c9 --- /dev/null +++ b/src/module/decode/state/ChangeChannelTimeoutEvent.java @@ -0,0 +1,29 @@ +package module.decode.state; + +import controller.channel.Channel.ChannelType; + +/** + * Allows decoders to change the call timeout setting in the channel state + */ +public class ChangeChannelTimeoutEvent extends DecoderStateEvent +{ + private ChannelType mChannelType; + private long mCallTimeout; + + public ChangeChannelTimeoutEvent( Object source, ChannelType channelType, long timeout ) + { + super( source, Event.CHANGE_CALL_TIMEOUT, State.IDLE ); + mChannelType = channelType; + mCallTimeout = timeout; + } + + public ChannelType getChannelType() + { + return mChannelType; + } + + public long getCallTimeout() + { + return mCallTimeout; + } +} \ No newline at end of file diff --git a/src/module/decode/state/ChannelCollectionPanel.java b/src/module/decode/state/ChannelCollectionPanel.java deleted file mode 100644 index eae3db84..00000000 --- a/src/module/decode/state/ChannelCollectionPanel.java +++ /dev/null @@ -1,154 +0,0 @@ -package module.decode.state; - -import java.awt.Component; -import java.awt.Point; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JMenu; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; -import settings.SettingsManager; -import controller.channel.Channel; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; - -/** - * Channel Collection assembles a primary channel panel and any related decoder - * state panels, provides contextual access to the channel and supports channel - * selection. - */ -public class ChannelCollectionPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( ChannelCollectionPanel.class ); - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private PlaylistManager mPlaylistManager; - private SettingsManager mSettingsManager; - - private List mChannelPanels = new ArrayList(); - - public ChannelCollectionPanel( ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SettingsManager settingsManager, - Channel channel ) - { - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; - mSettingsManager = settingsManager; - - init( channel ); - } - - protected void init( Channel channel ) - { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]0[]") ); - - setOpaque( false ); - - ChannelStatePanel csp = new ChannelStatePanel( mChannelModel, - mChannelProcessingManager, mPlaylistManager, mSettingsManager, channel ); - - mChannelPanels.add( csp ); - - add( csp, "span,grow" ); - - } - - public Channel getChannel() - { - if( mChannelPanels.size() >= 1 ) - { - return mChannelPanels.get( 0 ).getChannel(); - } - - return null; - } - - public void dispose() - { - for( ChannelStatePanel panel: mChannelPanels ) - { - panel.dispose(); - } - - mChannelPanels.clear(); - - mPlaylistManager = null; - mSettingsManager = null; - } - - /** - * Toggles the selection state of the channel if it matches the channel - * argument. Otherwise, sets the selection state to false. - */ - public void setSelectedChannel( Channel channel ) - { - for( ChannelStatePanel panel: mChannelPanels ) - { - if( panel.getChannel() == channel ) - { - /* Toggle the selection state */ - panel.getChannel().setSelected( !panel.getChannel().isSelected() ); - panel.repaint(); - } - else - { - if( panel.getChannel().isSelected() ) - { - panel.getChannel().setSelected( false ); - panel.repaint(); - } - } - } - } - - /** - * Returns the channel represented by the channel panel at the gui point - * location, to selecting the channel - * - * @param point - current location of the mouse - * - * @return - channel or null - */ - public Channel getChannelAt( Point point ) - { - Component c = this.getComponentAt( point ); - - if( c instanceof ChannelCollectionPanel ) - { - return ((ChannelCollectionPanel)c).getChannel(); - } - else - { - return getChannel(); - } - } - - protected SettingsManager getSettingsManager() - { - return mSettingsManager; - } - - - public JMenu getContextMenu() - { - if( !mChannelPanels.isEmpty() ) - { - return mChannelPanels.get( 0 ).getContextMenu(); - } - - return null; - } -} diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index be9f9a89..16fe4e4e 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -23,6 +23,7 @@ import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.HashMap; +import java.util.Map; import javax.swing.JMenu; import javax.swing.JPanel; @@ -42,6 +43,7 @@ import settings.ColorSettingResetMenuItem; import settings.SettingsManager; import controller.channel.Channel; import controller.channel.ChannelEvent; +import controller.channel.ChannelEvent.Event; import controller.channel.ChannelEventListener; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; @@ -55,8 +57,7 @@ public class ChannelList extends JPanel implements ChannelEventListener private final static Logger mLog = LoggerFactory.getLogger( ChannelList.class ); - private HashMap mDisplayedPanels = - new HashMap(); + private Map mDisplayedPanels = new HashMap(); private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; @@ -104,12 +105,12 @@ public class ChannelList extends JPanel implements ChannelEventListener { if( !mDisplayedPanels.containsKey( channel ) ) { - ChannelCollectionPanel panel = new ChannelCollectionPanel( mChannelModel, - mChannelProcessingManager, mPlaylistManager, mSettingsManager, channel ); + ChannelPanel channelPanel = new ChannelPanel( mChannelModel, + mChannelProcessingManager, mPlaylistManager, mSettingsManager, channel ); - add( panel, "wrap" ); + add( channelPanel, "wrap" ); - mDisplayedPanels.put( channel, panel ); + mDisplayedPanels.put( channel, channelPanel ); revalidate(); repaint(); @@ -120,7 +121,7 @@ public class ChannelList extends JPanel implements ChannelEventListener { if( mDisplayedPanels.containsKey( channel ) ) { - ChannelCollectionPanel panel = mDisplayedPanels.remove( channel ); + ChannelPanel panel = mDisplayedPanels.remove( channel ); if( panel != null ) { @@ -134,15 +135,6 @@ public class ChannelList extends JPanel implements ChannelEventListener } } - public void setSelectedChannel( Channel channel ) - { - /* Send channel selection to each channel panel */ - for( ChannelCollectionPanel panel: mDisplayedPanels.values() ) - { - panel.setSelectedChannel( channel ); - } - } - public class ListSelectionListener implements MouseListener { @Override @@ -150,18 +142,22 @@ public class ChannelList extends JPanel implements ChannelEventListener { Component component = getComponentAt( e.getPoint() ); - if( component instanceof ChannelCollectionPanel ) + if( component instanceof ChannelPanel ) { - ChannelCollectionPanel panel = (ChannelCollectionPanel)component; + ChannelPanel panel = (ChannelPanel)component; - Point translatedPoint = SwingUtilities - .convertPoint( e.getComponent(), e.getPoint(), panel ); - - Channel channel = panel.getChannelAt( translatedPoint ); + Channel channel = panel.getChannel(); if( e.getButton() == MouseEvent.BUTTON1 ) { - setSelectedChannel( channel ); + if( channel.isSelected() ) + { + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_DESELECT ) ); + } + else + { + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_SELECT ) ); + } } else if( e.getButton() == MouseEvent.BUTTON3 ) { diff --git a/src/module/decode/state/ChannelStatePanel.java b/src/module/decode/state/ChannelPanel.java similarity index 92% rename from src/module/decode/state/ChannelStatePanel.java rename to src/module/decode/state/ChannelPanel.java index 9b9dd7cb..0d88f826 100644 --- a/src/module/decode/state/ChannelStatePanel.java +++ b/src/module/decode/state/ChannelPanel.java @@ -15,13 +15,12 @@ import javax.swing.JMenu; import javax.swing.JPanel; import module.ProcessingChain; +import module.decode.DecoderFactory; +import net.miginfocom.swing.MigLayout; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import controller.channel.Channel; -import controller.channel.ChannelUtils; -import module.decode.DecoderFactory; -import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; import record.RecorderType; import record.config.RecordConfiguration; @@ -31,18 +30,22 @@ import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; import settings.SettingsManager; - import controller.channel.Channel; +import controller.channel.ChannelEvent; +import controller.channel.ChannelEvent.Event; +import controller.channel.ChannelEventListener; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; import controller.channel.ChannelUtils; -public class ChannelStatePanel extends JPanel - implements Listener, SettingChangeListener +public class ChannelPanel extends JPanel + implements Listener, + SettingChangeListener, + ChannelEventListener { private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( ChannelStatePanel.class ); + private final static Logger mLog = LoggerFactory.getLogger( ChannelPanel.class ); private Font mFontDetails = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); private Font mFontDecoder = new Font( Font.MONOSPACED, Font.PLAIN, 12 ); @@ -88,19 +91,16 @@ public class ChannelStatePanel extends JPanel * * Updates color background according to current channel state. */ - public ChannelStatePanel( ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SettingsManager settingsManager, - Channel channel ) + public ChannelPanel( ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + PlaylistManager playlistManager, + SettingsManager settingsManager, + Channel channel ) { mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; mPlaylistManager = playlistManager; - mSettingsManager = settingsManager; - mSettingsManager.addListener( this ); - mChannel = channel; ProcessingChain processingChain = @@ -109,9 +109,7 @@ public class ChannelStatePanel extends JPanel if( processingChain != null ) { mChannelState = processingChain.getChannelState(); - - mChannelState.setChangedAttributeListener( this ); - + init(); for( DecoderState decoderState: processingChain.getDecoderStates() ) @@ -120,6 +118,10 @@ public class ChannelStatePanel extends JPanel mDecoderPanels.add( panel ); add( panel, "grow,span" ); } + + mChannelState.setChangedAttributeListener( this ); + mChannelModel.addListener( this ); + mSettingsManager.addListener( this ); } else { @@ -191,6 +193,11 @@ public class ChannelStatePanel extends JPanel public void dispose() { + mChannelModel.removeListener( this ); + mChannelModel = null; + + mChannelProcessingManager = null; + mPlaylistManager = null; mSettingsManager.removeListener( this ); @@ -426,6 +433,16 @@ public class ChannelStatePanel extends JPanel public JMenu getContextMenu() { return ChannelUtils.getContextMenu( mChannelModel, mChannelProcessingManager, - mPlaylistManager, mChannel, ChannelStatePanel.this ); + mPlaylistManager, mChannel, ChannelPanel.this ); + } + + @Override + public void channelChanged( ChannelEvent event ) + { + if( event.getEvent() == Event.NOTIFICATION_SELECTION_CHANGE && + event.getChannel() == mChannel ) + { + repaint(); + } } } diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index 167f5180..4fd5186f 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -103,11 +103,8 @@ public class PlaylistManager implements ChannelEventListener mPlaylistLoading = true; - mLog.debug( "Transferring aliases to model" ); mAliasModel.addAliases( mPlaylist.getAliases() ); - mLog.debug( "Transferring channels to model" ); mChannelModel.addChannels( mPlaylist.getChannels() ); - mLog.debug( "Transfer to models is complete" ); mPlaylistLoading = false; } @@ -175,13 +172,9 @@ public class PlaylistManager implements ChannelEventListener { //TODO: recreate the playlist and load values from each of the models - mLog.debug( "Aliases in model:" + mAliasModel.getAliases().size() ); mPlaylist.setAliases( mAliasModel.getAliases() ); - mLog.debug( "Aliases in playlist:" + mPlaylist.getAliases().size() ); - mLog.debug( "Channels in model:" + mChannelModel.getChannels().size() ); mPlaylist.setChannels( mChannelModel.getChannels() ); - mLog.debug( "Channels in playlist:" + mPlaylist.getChannels().size() ); JAXBContext context = null; diff --git a/src/sample/complex/ComplexBufferToStreamConverter.java b/src/sample/complex/ComplexBufferToStreamConverter.java index ecce6561..16062a1c 100644 --- a/src/sample/complex/ComplexBufferToStreamConverter.java +++ b/src/sample/complex/ComplexBufferToStreamConverter.java @@ -1,5 +1,10 @@ package sample.complex; +import module.decode.p25.P25_LSMDecoder; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sample.Listener; /** @@ -7,9 +12,10 @@ import sample.Listener; */ public class ComplexBufferToStreamConverter implements Listener { + private final static Logger mLog = LoggerFactory.getLogger( ComplexBufferToStreamConverter.class ); + private ComplexSampleListener mListener; - public void dispose() { mListener = null; From e7d3713791ef19407fd41f06c474acd7387bc87a Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 07:32:26 -0500 Subject: [PATCH 17/74] Removes message broadcaster from Decoder abstract class -- processing chain has a message broadcaster built in, so the decoder's broadcaster is redundant. Changes Decoder(s) to only have a single registered message listener (ie processing chain) --- src/message/IMessageProvider.java | 4 +-- src/module/ProcessingChain.java | 2 +- src/module/decode/Decoder.java | 36 +++++++++++++------ .../decode/fleetsync2/Fleetsync2Decoder.java | 2 +- src/module/decode/lj1200/LJ1200Decoder.java | 2 +- src/module/decode/ltrnet/LTRNetDecoder.java | 2 +- .../ltrstandard/LTRStandardDecoder.java | 2 +- src/module/decode/mdc1200/MDCDecoder.java | 2 +- src/module/decode/mpt1327/MPT1327Decoder.java | 2 +- src/module/decode/p25/P25Decoder.java | 2 +- .../decode/passport/PassportDecoder.java | 2 +- src/module/decode/tait/Tait1200Decoder.java | 4 +-- 12 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/message/IMessageProvider.java b/src/message/IMessageProvider.java index 4618020a..41b29ac3 100644 --- a/src/message/IMessageProvider.java +++ b/src/message/IMessageProvider.java @@ -4,6 +4,6 @@ import sample.Listener; public interface IMessageProvider { - public void addMessageListener( Listener listener ); - public void removeMessageListener( Listener listener ); + public void setMessageListener( Listener listener ); + public void removeMessageListener(); } diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index af501560..5cdc154a 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -401,7 +401,7 @@ public class ProcessingChain implements IChannelEventListener if( module instanceof IMessageProvider ) { - ((IMessageProvider)module).addMessageListener( mMessageBroadcaster ); + ((IMessageProvider)module).setMessageListener( mMessageBroadcaster ); } if( module instanceof IMetadataProvider ) diff --git a/src/module/decode/Decoder.java b/src/module/decode/Decoder.java index 80cc19b2..cc6880a7 100644 --- a/src/module/decode/Decoder.java +++ b/src/module/decode/Decoder.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,13 +20,13 @@ package module.decode; import message.IMessageProvider; import message.Message; import module.Module; -import sample.Broadcaster; import sample.Listener; -public abstract class Decoder extends Module implements IMessageProvider +public abstract class Decoder extends Module + implements IMessageProvider, Listener { /* This has to be a broadcaster in order for references to persist */ - protected Broadcaster mMessageBroadcaster = new Broadcaster<>(); + protected Listener mMessageListener; /** * Decoder - parent class for all decoders, demodulators and components. @@ -38,8 +38,7 @@ public abstract class Decoder extends Module implements IMessageProvider @Override public void dispose() { - mMessageBroadcaster.dispose(); - mMessageBroadcaster = null; + mMessageListener = null; } /** @@ -52,9 +51,9 @@ public abstract class Decoder extends Module implements IMessageProvider * Adds a listener for receiving decoded messages from this decoder */ @Override - public void addMessageListener( Listener listener ) + public void setMessageListener( Listener listener ) { - mMessageBroadcaster.addListener( listener ); + mMessageListener = listener; } /** @@ -62,8 +61,25 @@ public abstract class Decoder extends Module implements IMessageProvider * decoders */ @Override - public void removeMessageListener( Listener listener ) + public void removeMessageListener() { - mMessageBroadcaster.removeListener( listener ); + mMessageListener = null; } + + /** + * Broadcasts the message to the registered message listener + */ + public void broadcast( Message message ) + { + if( mMessageListener != null ) + { + mMessageListener.receive( message ); + } + } + + @Override + public void receive( Message message ) + { + broadcast( message ); + } } diff --git a/src/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java index f8092472..43243de6 100644 --- a/src/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -90,7 +90,7 @@ public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferLis mMessageProcessor = new Fleetsync2MessageProcessor( aliasList ); mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); } public void dispose() diff --git a/src/module/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java index 1e20eeda..5b33ed07 100644 --- a/src/module/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -100,7 +100,7 @@ public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListene mMessageProcessor = new LJ1200MessageProcessor( aliasList ); mTowerMessageFramer.addMessageListener( mMessageProcessor ); mTransponderMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); } @Override diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java index ff12edca..544d8cf6 100644 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -74,7 +74,7 @@ public class LTRNetDecoder extends Decoder mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + mLTRMessageProcessor.setMessageListener( this ); } @Override diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java index 365bd74c..0324e029 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -62,7 +62,7 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer mLTRMessageProcessor = new LTRStandardMessageProcessor( direction, aliasList ); mLTRMessageFramer.addMessageListener( mLTRMessageProcessor ); - mLTRMessageProcessor.setMessageListener( mMessageBroadcaster ); + mLTRMessageProcessor.setMessageListener( this ); } @Override diff --git a/src/module/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java index 7e973af2..d4351d56 100644 --- a/src/module/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -102,7 +102,7 @@ public class MDCDecoder extends Decoder implements IFilteredRealBufferListener, /* Message Processor */ mMessageProcessor = new MDCMessageProcessor( aliasList ); mMessageFramer.addMessageListener( mMessageProcessor ); - mMessageProcessor.addMessageListener( mMessageBroadcaster ); + mMessageProcessor.addMessageListener( this ); } public void dispose() diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java index 45bc3372..9363b19c 100644 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -140,7 +140,7 @@ public class MPT1327Decoder extends Decoder /* Fully decoded and framed messages processor */ mMessageProcessor = new MPT1327MessageProcessor( aliasList ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); mControlMessageFramer.addMessageListener( mMessageProcessor ); mTrafficMessageFramer.addMessageListener( mMessageProcessor ); } diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java index ca46e81b..5c879e5e 100644 --- a/src/module/decode/p25/P25Decoder.java +++ b/src/module/decode/p25/P25Decoder.java @@ -12,7 +12,7 @@ public abstract class P25Decoder extends Decoder implements Instrumentable public P25Decoder( AliasList aliasList ) { mMessageProcessor = new P25MessageProcessor( aliasList ); - mMessageProcessor.setMessageListener( mMessageBroadcaster ); + mMessageProcessor.setMessageListener( this ); } public void dispose() diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java index 46381d8d..ddae98ed 100644 --- a/src/module/decode/passport/PassportDecoder.java +++ b/src/module/decode/passport/PassportDecoder.java @@ -63,7 +63,7 @@ public class PassportDecoder extends Decoder mPassportMessageProcessor = new PassportMessageProcessor( aliasList ); mPassportMessageFramer.addMessageListener( mPassportMessageProcessor ); - mPassportMessageProcessor.setMessageListener( mMessageBroadcaster ); + mPassportMessageProcessor.setMessageListener( this ); } @Override diff --git a/src/module/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java index 8f21e80d..9f19f99a 100644 --- a/src/module/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -104,8 +104,8 @@ public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListe mMessageFramerGPS.addMessageListener( mMessageAProcessor ); mMessageFramerANI.addMessageListener( mMessageBProcessor ); - mMessageAProcessor.setMessageListener( mMessageBroadcaster ); - mMessageBProcessor.setMessageListener( mMessageBroadcaster ); + mMessageAProcessor.setMessageListener( this ); + mMessageBProcessor.setMessageListener( this ); } public void dispose() From e2a0451bfb0c6355f3a1474735f8a600148a4827 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 08:42:29 -0500 Subject: [PATCH 18/74] Resolves issue with scheduled thread pool overtasking. Removes tuner channel source decimation processing over to its own thread pool. --- .../channel/NameConfigurationEditor.java | 1 - src/module/ProcessingChain.java | 5 +++ src/sample/Broadcaster.java | 14 +++---- src/source/SourceManager.java | 2 +- src/source/recording/Recording.java | 5 +-- .../recording/RecordingSourceManager.java | 7 +--- src/source/tuner/ITunerChannelProvider.java | 5 +-- src/source/tuner/Tuner.java | 4 +- src/source/tuner/TunerChannelSource.java | 37 ++++++++++++------- src/source/tuner/TunerController.java | 7 ++-- src/source/tuner/TunerManager.java | 2 +- src/source/tuner/airspy/AirspyTuner.java | 7 ++-- src/source/tuner/ettus/B100Tuner.java | 4 +- src/source/tuner/fcd/FCDTuner.java | 8 ++-- src/source/tuner/hackrf/HackRFTuner.java | 7 ++-- src/source/tuner/rtl/RTL2832Tuner.java | 5 +-- 16 files changed, 62 insertions(+), 58 deletions(-) diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java index fe30282b..3101e12e 100644 --- a/src/controller/channel/NameConfigurationEditor.java +++ b/src/controller/channel/NameConfigurationEditor.java @@ -243,7 +243,6 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor mChannelName.setText( DEFAULT_NAME ); mSystemNameCombo.setModel( EMPTY_MODEL ); mSiteNameCombo.setModel( EMPTY_MODEL ); - mAliasListCombo.setSelectedIndex( 0 ); } } } diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 5cdc154a..046d8c51 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -495,6 +495,11 @@ public class ProcessingChain implements IChannelEventListener tcs.start(); } + + for( Listener listener: mUnFilteredRealBufferBroadcaster.getListeners() ) + { + mLog.debug( "*** Listener:" + listener.getClass() ); + } } else { diff --git a/src/sample/Broadcaster.java b/src/sample/Broadcaster.java index 35fcefa4..b779bc41 100644 --- a/src/sample/Broadcaster.java +++ b/src/sample/Broadcaster.java @@ -17,10 +17,9 @@ ******************************************************************************/ package sample; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import module.decode.state.ChannelState; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,11 +28,7 @@ import org.slf4j.LoggerFactory; */ public class Broadcaster implements Listener { - private final static Logger mLog = - LoggerFactory.getLogger( Broadcaster.class ); - - private CopyOnWriteArrayList> mListeners = - new CopyOnWriteArrayList>(); + private List> mListeners = new CopyOnWriteArrayList<>(); @Override public void receive( T t ) @@ -59,6 +54,11 @@ public class Broadcaster implements Listener return mListeners.size(); } + public List> getListeners() + { + return mListeners; + } + public void addListener( Listener listener ) { mListeners.add( listener ); diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index 9fab09f4..c4a06e19 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -37,7 +37,7 @@ public class SourceManager ThreadPoolManager threadPoolManager ) { mMixerManager = new MixerManager(); - mRecordingSourceManager = new RecordingSourceManager( settingsManager, threadPoolManager ); + mRecordingSourceManager = new RecordingSourceManager( settingsManager ); mTunerManager = new TunerManager( mMixerManager, settingsManager, threadPoolManager ); } diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index a3b1aba7..bfbe790e 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -64,9 +64,8 @@ public class Recording implements Comparable, } @Override - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, - SourceException + public TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException { // TODO Auto-generated method stub return null; diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index 8c2f444f..a047b170 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -41,13 +41,10 @@ public class RecordingSourceManager private ArrayList mRecordings = new ArrayList(); private SettingsManager mSettingsManager; - private ThreadPoolManager mThreadPoolManager; - public RecordingSourceManager( SettingsManager settingsManager, - ThreadPoolManager threadPoolManager ) + public RecordingSourceManager( SettingsManager settingsManager ) { mSettingsManager = settingsManager; - mThreadPoolManager = threadPoolManager; loadRecordings(); } @@ -73,7 +70,7 @@ public class RecordingSourceManager if( recording != null ) { - retVal = recording.getChannel( mThreadPoolManager, tunerChannel ); + retVal = recording.getChannel( tunerChannel ); } else { diff --git a/src/source/tuner/ITunerChannelProvider.java b/src/source/tuner/ITunerChannelProvider.java index 06f7b2fa..b3a69a63 100644 --- a/src/source/tuner/ITunerChannelProvider.java +++ b/src/source/tuner/ITunerChannelProvider.java @@ -20,7 +20,6 @@ package source.tuner; import java.util.concurrent.RejectedExecutionException; import source.SourceException; -import controller.ThreadPoolManager; public interface ITunerChannelProvider { @@ -31,8 +30,8 @@ public interface ITunerChannelProvider * * @return - source for 48k sample rate */ - public abstract TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, SourceException; + public abstract TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException; /** * Releases the tuned channel resources diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 805a3cb5..e5769b54 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -145,8 +145,8 @@ public abstract class Tuner implements IFrequencyChangeProcessor, * * @return - source for 48k sample rate */ - public abstract TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, SourceException; + public abstract TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException; /** * Releases the tuned channel resources diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 010a6f7b..f75e95e7 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -19,8 +19,10 @@ package source.tuner; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,7 +39,7 @@ import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; -import controller.ThreadPoolManager; +import controller.NamingThreadFactory; import controller.ThreadPoolManager.ThreadType; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; @@ -61,7 +63,7 @@ public class TunerChannelSource extends ComplexSource private ComplexPrimeCICDecimate mDecimationFilter; private Listener mListener; private IFrequencyChangeProcessor mFrequencyChangeProcessor; - private ThreadPoolManager mThreadPoolManager; + private ScheduledExecutorService mScheduledExecutorService; private ScheduledFuture mTaskHandle; private long mTunerFrequency = 0; @@ -91,12 +93,9 @@ public class TunerChannelSource extends ComplexSource * accept the decimation processing task * @throws SourceException if the tuner has an issue providing IQ samples */ - public TunerChannelSource( ThreadPoolManager threadPoolManager, Tuner tuner, - TunerChannel tunerChannel ) + public TunerChannelSource( Tuner tuner, TunerChannel tunerChannel ) throws RejectedExecutionException, SourceException { - mThreadPoolManager = threadPoolManager; - mTuner = tuner; mTuner.addListener( this ); @@ -133,9 +132,12 @@ public class TunerChannelSource extends ComplexSource if( mRunning.compareAndSet( false, true )) { - /* Schedule the decimation task to run every 20 ms (50 iterations/second) */ - mTaskHandle = mThreadPoolManager.scheduleFixedRate( ThreadType.DECIMATION, - mDecimationProcessor, 20, TimeUnit.MILLISECONDS ); + mScheduledExecutorService = Executors.newScheduledThreadPool( 1, + new NamingThreadFactory( "tuner channel source" ) ); + + /* Schedule the decimation task to run every 10 ms (100 iterations/second) */ + mTaskHandle = mScheduledExecutorService.scheduleAtFixedRate( + mDecimationProcessor, 0, 10, TimeUnit.MILLISECONDS ); /* Finally, register to receive samples from the tuner */ mTuner.addListener( (Listener)this ); @@ -156,10 +158,15 @@ public class TunerChannelSource extends ComplexSource mTuner.releaseChannel( this ); mDecimationProcessor.shutdown(); - if( mThreadPoolManager != null && mTaskHandle != null ) + if( mScheduledExecutorService != null && mTaskHandle != null ) { - mThreadPoolManager.cancel( mTaskHandle ); - mTaskHandle = null; + if( mTaskHandle != null ) + { + mTaskHandle.cancel( true ); + mTaskHandle = null; + } + + mScheduledExecutorService.shutdownNow(); } mBuffer.clear(); @@ -340,7 +347,9 @@ public class TunerChannelSource extends ComplexSource { if( mBuffer != null ) { - mBuffer.drainTo( mSampleBuffers, 4 ); + long start = System.currentTimeMillis(); + + mBuffer.drainTo( mSampleBuffers ); for( Buffer buffer: mSampleBuffers ) { @@ -378,6 +387,8 @@ public class TunerChannelSource extends ComplexSource } } + mLog.debug( "Processed [" + mSampleBuffers.size() + "] in " + ( System.currentTimeMillis() - start ) ); + mSampleBuffers.clear(); } } diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index faf36961..93c22339 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -194,8 +194,8 @@ public abstract class TunerController implements Tunable * * @throws RejectedExecutionException if the decimation processor has an error */ - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - Tuner tuner, TunerChannel channel ) throws RejectedExecutionException + public TunerChannelSource getChannel( Tuner tuner, TunerChannel channel ) + throws RejectedExecutionException { TunerChannelSource source = null; @@ -210,8 +210,7 @@ public abstract class TunerController implements Tunable updateLOFrequency(); } - source = new TunerChannelSource( threadPoolManager, - tuner, channel ); + source = new TunerChannelSource( tuner, channel ); } catch( SourceException se ) { diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index 3d6f351c..29b87fb8 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -104,7 +104,7 @@ public class TunerManager try { - retVal = tuner.getChannel( mThreadPoolManager, tunerChannel ); + retVal = tuner.getChannel( tunerChannel ); } catch ( RejectedExecutionException ree ) { diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 345b3456..72fe4506 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -35,7 +35,6 @@ import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.BoardID; -import controller.ThreadPoolManager; public class AirspyTuner extends Tuner { @@ -128,8 +127,8 @@ public class AirspyTuner extends Tuner } @Override - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, SourceException + public TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException { //TODO: this channel has a decimated sample rate of: // 10.0 MSps = 10,000,000 / 208 = 48076.923 @@ -138,7 +137,7 @@ public class AirspyTuner extends Tuner //Consider implementing a fractional resampler to get a correct 48 kHz //output sample rate - return mController.getChannel( threadPoolManager, this, channel ); + return mController.getChannel( this, channel ); } @Override diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java index 11cbc94f..51cc2ba3 100644 --- a/src/source/tuner/ettus/B100Tuner.java +++ b/src/source/tuner/ettus/B100Tuner.java @@ -99,8 +99,8 @@ public class B100Tuner extends Tuner } @Override - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, SourceException + public TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException { //TODO: pass this call to the tuner controller return null; diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index e3d680a5..7c304c61 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -33,7 +33,6 @@ import source.tuner.TunerChannelSource; import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; -import controller.ThreadPoolManager; public class FCDTuner extends MixerTuner { @@ -111,11 +110,10 @@ public class FCDTuner extends MixerTuner } @Override - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel tunerChannel ) throws RejectedExecutionException, - SourceException + public TunerChannelSource getChannel( TunerChannel tunerChannel ) + throws RejectedExecutionException, SourceException { - return mController.getChannel( threadPoolManager, this, tunerChannel ); + return mController.getChannel( this, tunerChannel ); } /** diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java index 154c55bc..308e67c4 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -35,7 +35,6 @@ import source.tuner.TunerClass; import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.hackrf.HackRFTunerController.BoardID; -import controller.ThreadPoolManager; public class HackRFTuner extends Tuner { @@ -106,10 +105,10 @@ public class HackRFTuner extends Tuner } @Override - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) throws RejectedExecutionException, SourceException + public TunerChannelSource getChannel( TunerChannel channel ) + throws RejectedExecutionException, SourceException { - return mController.getChannel( threadPoolManager, this, channel ); + return mController.getChannel( this, channel ); } @Override diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 4af1a8d7..39b6f0f1 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -148,8 +148,7 @@ public class RTL2832Tuner extends Tuner } @Override - public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, - TunerChannel channel ) + public TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException { @@ -157,7 +156,7 @@ public class RTL2832Tuner extends Tuner try { - source = mController.getChannel( threadPoolManager, this, channel ); + source = mController.getChannel( this, channel ); } catch( Exception e ) { From f7f6c2fc3e1f02f1b13fc3b9f5080a499d21934c Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 15:47:14 -0500 Subject: [PATCH 19/74] Merges changes from master branch to resolve issue with CPU and memory and constraints on scheduled thread pool saturation. Adds a new scheduled thread pool for each processing chain that the components in the chain can share, versus the single shared pool exposed by the threadpoolmanager class. --- src/audio/AudioModule.java | 3 +- src/controller/ThreadPoolManager.java | 5 ++ .../channel/ChannelProcessingManager.java | 2 +- .../InstrumentableProcessingChain.java | 2 +- src/module/Module.java | 6 +- src/module/ProcessingChain.java | 72 +++++++++---------- src/module/decode/am/AMDecoder.java | 4 +- .../decode/fleetsync2/Fleetsync2Decoder.java | 3 +- .../fleetsync2/Fleetsync2DecoderState.java | 3 +- src/module/decode/lj1200/LJ1200Decoder.java | 3 +- .../decode/lj1200/LJ1200DecoderState.java | 3 +- src/module/decode/ltrnet/LTRNetDecoder.java | 3 +- .../decode/ltrnet/LTRNetDecoderState.java | 3 +- .../ltrstandard/LTRStandardDecoder.java | 4 +- .../ltrstandard/LTRStandardDecoderState.java | 3 +- src/module/decode/mdc1200/MDCDecoder.java | 3 +- .../decode/mdc1200/MDCDecoderState.java | 3 +- src/module/decode/mpt1327/MPT1327Decoder.java | 3 +- .../decode/mpt1327/MPT1327DecoderState.java | 3 +- src/module/decode/nbfm/NBFMDecoder.java | 4 +- src/module/decode/p25/P25DecoderState.java | 3 +- src/module/decode/p25/P25_C4FMDecoder.java | 3 +- src/module/decode/p25/P25_LSMDecoder.java | 3 +- .../decode/p25/audio/P25AudioModule.java | 4 +- .../decode/passport/PassportDecoder.java | 3 +- .../decode/passport/PassportDecoderState.java | 3 +- .../state/AlwaysUnsquelchedDecoderState.java | 4 +- src/module/decode/state/ChannelState.java | 14 ++-- .../decode/state/TrafficChannelManager.java | 13 +++- src/module/decode/tait/Tait1200Decoder.java | 3 +- .../decode/tait/Tait1200DecoderState.java | 3 +- .../demodulate/am/AMDemodulatorModule.java | 4 +- .../audio/DemodulatedAudioFilterModule.java | 4 +- .../demodulate/fm/FMDemodulatorModule.java | 4 +- src/module/log/CallEventLogger.java | 1 - src/module/log/EventLogger.java | 4 +- src/module/log/MessageEventLogger.java | 1 - src/record/RecorderManager.java | 8 +-- .../wave/ComplexBufferWaveRecorder.java | 23 +++--- src/record/wave/RealBufferWaveRecorder.java | 26 +++---- src/source/mixer/RealMixerSource.java | 5 +- src/source/tuner/TunerChannelSource.java | 37 +++------- .../frequency/DCTrackingFrequencyControl.java | 4 +- src/source/wave/ComplexWaveSource.java | 5 +- src/source/wave/RealWaveSource.java | 5 +- 45 files changed, 170 insertions(+), 152 deletions(-) diff --git a/src/audio/AudioModule.java b/src/audio/AudioModule.java index 1f87f38d..01ad02d4 100644 --- a/src/audio/AudioModule.java +++ b/src/audio/AudioModule.java @@ -1,6 +1,7 @@ package audio; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import module.Module; @@ -72,7 +73,7 @@ public class AudioModule extends Module implements IAudioPacketProvider, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { /* No start operations provided */ } diff --git a/src/controller/ThreadPoolManager.java b/src/controller/ThreadPoolManager.java index c076422a..ca993295 100644 --- a/src/controller/ThreadPoolManager.java +++ b/src/controller/ThreadPoolManager.java @@ -41,6 +41,11 @@ public class ThreadPoolManager public ThreadPoolManager() { } + + public ScheduledExecutorService getScheduledExecutorService() + { + return mExecutor; + } public ScheduledFuture scheduleFixedRate( ThreadType type, Runnable command, diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index d1a5f423..dac06b4d 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -185,7 +185,7 @@ public class ChannelProcessingManager implements ChannelEventListener if( processingChain == null ) { - processingChain = new ProcessingChain( mThreadPoolManager, + processingChain = new ProcessingChain( channel.getName(), channel.getChannelType() ); /* Register global listeners */ diff --git a/src/instrument/InstrumentableProcessingChain.java b/src/instrument/InstrumentableProcessingChain.java index 94181b18..882eb891 100644 --- a/src/instrument/InstrumentableProcessingChain.java +++ b/src/instrument/InstrumentableProcessingChain.java @@ -15,7 +15,7 @@ public class InstrumentableProcessingChain extends ProcessingChain implements In { public InstrumentableProcessingChain() { - super( ChannelType.STANDARD ); + super( "instrumented", ChannelType.STANDARD ); } public void setSource( Source source ) throws IllegalStateException diff --git a/src/module/Module.java b/src/module/Module.java index 87295327..a46b3e9c 100644 --- a/src/module/Module.java +++ b/src/module/Module.java @@ -18,6 +18,8 @@ package module; +import java.util.concurrent.ScheduledExecutorService; + public abstract class Module { /** @@ -57,9 +59,9 @@ public abstract class Module public abstract void reset(); /** - * Start processing. + * Start processing. Provides an executor to use for scheduling tasks */ - public abstract void start(); + public abstract void start( ScheduledExecutorService executor ); /** * Stop processing diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 046d8c51..e263a920 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -19,7 +19,8 @@ package module; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import message.IMessageListener; @@ -67,6 +68,7 @@ import audio.metadata.Metadata; import audio.squelch.ISquelchStateListener; import audio.squelch.ISquelchStateProvider; import audio.squelch.SquelchState; +import controller.NamingThreadFactory; import controller.ThreadPoolManager; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; @@ -108,8 +110,8 @@ public class ProcessingChain implements IChannelEventListener private Broadcaster mUnFilteredRealBufferBroadcaster = new Broadcaster<>(); private Broadcaster mSquelchStateBroadcaster = new Broadcaster<>(); - private ThreadPoolManager mThreadPoolManager; - private ScheduledFuture mBufferProcessingTask; + private ScheduledExecutorService mScheduledExecutorService; + private String mName; private AtomicBoolean mRunning = new AtomicBoolean(); protected Source mSource; @@ -117,19 +119,18 @@ public class ProcessingChain implements IChannelEventListener private CallEventModel mCallEventModel; private ChannelState mChannelState; private MessageActivityModel mMessageActivityModel; - - public ProcessingChain( ChannelType channelType ) + + /** + * Creates a processing chain for managing a set of modules + * + * @param name for thread pool naming each thread + * @param channelType + */ + public ProcessingChain( String name, ChannelType channelType ) { - this( new ThreadPoolManager(), channelType ); - } - - - public ProcessingChain( ThreadPoolManager threadManager, - ChannelType channelType ) - { - mThreadPoolManager = threadManager; - - mChannelState = new ChannelState( mThreadPoolManager, channelType ); + mName = name; + + mChannelState = new ChannelState( channelType ); addModule( mChannelState ); mCallEventModel = new CallEventModel(); @@ -169,10 +170,6 @@ public class ProcessingChain implements IChannelEventListener mModules.clear(); - - mThreadPoolManager = null; - mBufferProcessingTask = null; - mAudioPacketBroadcaster.dispose(); mCallEventBroadcaster.dispose(); mChannelEventBroadcaster.dispose(); @@ -445,19 +442,6 @@ public class ProcessingChain implements IChannelEventListener module.reset(); } - /* Start each of the modules */ - for( Module module: mModules ) - { - try - { - module.start(); - } - catch( Exception e ) - { - mLog.error( "Error starting module", e ); - } - } - /* Register with the source to receive sample data. Setup a * timer task to process the buffer queues 50 times a second * (every 20 ms) */ @@ -492,8 +476,22 @@ public class ProcessingChain implements IChannelEventListener { mLog.error( "Error getting frequency from tuner channel source", e ); } - - tcs.start(); + } + + mScheduledExecutorService = Executors.newScheduledThreadPool( + 1, new NamingThreadFactory( "channel " + mName ) ); + + /* Start each of the modules */ + for( Module module: mModules ) + { + try + { + module.start( mScheduledExecutorService ); + } + catch( Exception e ) + { + mLog.error( "Error starting module", e ); + } } for( Listener listener: mUnFilteredRealBufferBroadcaster.getListeners() ) @@ -536,12 +534,6 @@ public class ProcessingChain implements IChannelEventListener mSource = null; } - if( mBufferProcessingTask != null ) - { - mThreadPoolManager.cancel( mBufferProcessingTask ); - mBufferProcessingTask = null; - } - /* Stop each of the modules */ for( Module module: mModules ) { diff --git a/src/module/decode/am/AMDecoder.java b/src/module/decode/am/AMDecoder.java index 8b407b67..c5b371e3 100644 --- a/src/module/decode/am/AMDecoder.java +++ b/src/module/decode/am/AMDecoder.java @@ -18,6 +18,8 @@ package module.decode.am; +import java.util.concurrent.ScheduledExecutorService; + import module.decode.DecoderType; import module.decode.PrimaryDecoder; import module.decode.config.DecodeConfiguration; @@ -41,7 +43,7 @@ public class AMDecoder extends PrimaryDecoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/fleetsync2/Fleetsync2Decoder.java b/src/module/decode/fleetsync2/Fleetsync2Decoder.java index 43243de6..5b18628b 100644 --- a/src/module/decode/fleetsync2/Fleetsync2Decoder.java +++ b/src/module/decode/fleetsync2/Fleetsync2Decoder.java @@ -25,6 +25,7 @@ import instrument.tap.stream.FloatBufferTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -180,7 +181,7 @@ public class Fleetsync2Decoder extends Decoder implements IFilteredRealBufferLis } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java index 8fb2cd00..bb5d614d 100644 --- a/src/module/decode/fleetsync2/Fleetsync2DecoderState.java +++ b/src/module/decode/fleetsync2/Fleetsync2DecoderState.java @@ -19,6 +19,7 @@ package module.decode.fleetsync2; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -58,7 +59,7 @@ public class Fleetsync2DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/lj1200/LJ1200Decoder.java b/src/module/decode/lj1200/LJ1200Decoder.java index 5b33ed07..3e12b544 100644 --- a/src/module/decode/lj1200/LJ1200Decoder.java +++ b/src/module/decode/lj1200/LJ1200Decoder.java @@ -26,6 +26,7 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -198,7 +199,7 @@ public class LJ1200Decoder extends Decoder implements IFilteredRealBufferListene } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/lj1200/LJ1200DecoderState.java b/src/module/decode/lj1200/LJ1200DecoderState.java index 71b72f0f..1069068f 100644 --- a/src/module/decode/lj1200/LJ1200DecoderState.java +++ b/src/module/decode/lj1200/LJ1200DecoderState.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,7 +59,7 @@ public class LJ1200DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrnet/LTRNetDecoder.java b/src/module/decode/ltrnet/LTRNetDecoder.java index 544d8cf6..632a967d 100644 --- a/src/module/decode/ltrnet/LTRNetDecoder.java +++ b/src/module/decode/ltrnet/LTRNetDecoder.java @@ -23,6 +23,7 @@ import instrument.tap.TapGroup; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import message.MessageDirection; import module.decode.Decoder; @@ -120,7 +121,7 @@ public class LTRNetDecoder extends Decoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrnet/LTRNetDecoderState.java b/src/module/decode/ltrnet/LTRNetDecoderState.java index 245f7ace..6f17d77a 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderState.java +++ b/src/module/decode/ltrnet/LTRNetDecoderState.java @@ -21,6 +21,7 @@ import java.text.DecimalFormat; import java.util.HashMap; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -777,7 +778,7 @@ public class LTRNetDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoder.java b/src/module/decode/ltrstandard/LTRStandardDecoder.java index 0324e029..b04f0e47 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoder.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoder.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.decode.ltrstandard; +import java.util.concurrent.ScheduledExecutorService; + import message.MessageDirection; import module.decode.Decoder; import module.decode.DecoderType; @@ -83,7 +85,7 @@ public class LTRStandardDecoder extends Decoder implements IUnFilteredRealBuffer } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderState.java b/src/module/decode/ltrstandard/LTRStandardDecoderState.java index 94cd32b9..9443660a 100644 --- a/src/module/decode/ltrstandard/LTRStandardDecoderState.java +++ b/src/module/decode/ltrstandard/LTRStandardDecoderState.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -61,7 +62,7 @@ public class LTRStandardDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/mdc1200/MDCDecoder.java b/src/module/decode/mdc1200/MDCDecoder.java index d4351d56..89f04afc 100644 --- a/src/module/decode/mdc1200/MDCDecoder.java +++ b/src/module/decode/mdc1200/MDCDecoder.java @@ -26,6 +26,7 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -210,7 +211,7 @@ public class MDCDecoder extends Decoder implements IFilteredRealBufferListener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/mdc1200/MDCDecoderState.java b/src/module/decode/mdc1200/MDCDecoderState.java index f896a429..0a9d3a7c 100644 --- a/src/module/decode/mdc1200/MDCDecoderState.java +++ b/src/module/decode/mdc1200/MDCDecoderState.java @@ -19,6 +19,7 @@ package module.decode.mdc1200; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -54,7 +55,7 @@ public class MDCDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/mpt1327/MPT1327Decoder.java b/src/module/decode/mpt1327/MPT1327Decoder.java index 9363b19c..e7ea5b8a 100644 --- a/src/module/decode/mpt1327/MPT1327Decoder.java +++ b/src/module/decode/mpt1327/MPT1327Decoder.java @@ -26,6 +26,7 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -306,7 +307,7 @@ public class MPT1327Decoder extends Decoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/mpt1327/MPT1327DecoderState.java b/src/module/decode/mpt1327/MPT1327DecoderState.java index a5b94f27..20858552 100644 --- a/src/module/decode/mpt1327/MPT1327DecoderState.java +++ b/src/module/decode/mpt1327/MPT1327DecoderState.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -85,7 +86,7 @@ public class MPT1327DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/nbfm/NBFMDecoder.java b/src/module/decode/nbfm/NBFMDecoder.java index 1e715288..1b055a91 100644 --- a/src/module/decode/nbfm/NBFMDecoder.java +++ b/src/module/decode/nbfm/NBFMDecoder.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.decode.nbfm; +import java.util.concurrent.ScheduledExecutorService; + import module.decode.DecoderType; import module.decode.PrimaryDecoder; import module.decode.config.DecodeConfiguration; @@ -40,7 +42,7 @@ public class NBFMDecoder extends PrimaryDecoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 55340dba..5fce8640 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -204,7 +205,7 @@ public class P25DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index 3bae443d..09d6e45d 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -25,6 +25,7 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -229,7 +230,7 @@ public class P25_C4FMDecoder extends P25Decoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index 7614ac24..b6e64bf3 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -26,6 +26,7 @@ import instrument.tap.stream.QPSKTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -222,7 +223,7 @@ public class P25_LSMDecoder extends P25Decoder implements IComplexBufferListener } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/p25/audio/P25AudioModule.java b/src/module/decode/p25/audio/P25AudioModule.java index 6e5bfdf9..68496892 100644 --- a/src/module/decode/p25/audio/P25AudioModule.java +++ b/src/module/decode/p25/audio/P25AudioModule.java @@ -1,5 +1,7 @@ package module.decode.p25.audio; +import java.util.concurrent.ScheduledExecutorService; + import jmbe.iface.AudioConversionLibrary; import jmbe.iface.AudioConverter; import message.IMessageListener; @@ -78,7 +80,7 @@ public class P25AudioModule extends Module implements Listener, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/passport/PassportDecoder.java b/src/module/decode/passport/PassportDecoder.java index ddae98ed..ceb2291d 100644 --- a/src/module/decode/passport/PassportDecoder.java +++ b/src/module/decode/passport/PassportDecoder.java @@ -23,6 +23,7 @@ import instrument.tap.TapGroup; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -109,7 +110,7 @@ public class PassportDecoder extends Decoder } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/passport/PassportDecoderState.java b/src/module/decode/passport/PassportDecoderState.java index 003d2318..99004efd 100644 --- a/src/module/decode/passport/PassportDecoderState.java +++ b/src/module/decode/passport/PassportDecoderState.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import message.Message; import module.decode.DecoderType; @@ -75,7 +76,7 @@ public class PassportDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java index ad74e2f8..5fd65adb 100644 --- a/src/module/decode/state/AlwaysUnsquelchedDecoderState.java +++ b/src/module/decode/state/AlwaysUnsquelchedDecoderState.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.decode.state; +import java.util.concurrent.ScheduledExecutorService; + import message.Message; import module.decode.DecoderType; import module.decode.state.DecoderStateEvent.Event; @@ -90,7 +92,7 @@ public class AlwaysUnsquelchedDecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { broadcast( new Metadata( MetadataType.TO, mChannelName, false ) ); broadcast( new DecoderStateEvent( this, Event.ALWAYS_UNSQUELCH, State.IDLE ) ); diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java index 450ed92d..836531a7 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/module/decode/state/ChannelState.java @@ -18,6 +18,7 @@ package module.decode.state; import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -74,7 +75,6 @@ public class ChannelState extends Module implements ICallEventProvider, private AtomicBoolean mMonitoring = new AtomicBoolean(); private ScheduledFuture mStateMonitorFuture; - private ThreadPoolManager mThreadPoolManager; private ChannelType mChannelType; private TrafficChannelStatusListener mTrafficChannelStatusListener; @@ -106,9 +106,8 @@ public class ChannelState extends Module implements ICallEventProvider, * ended, while continuing to display the call details for the user * TEARDOWN Indicates a traffic channel that will be torn down for reuse. */ - public ChannelState( ThreadPoolManager manager, ChannelType channelType ) + public ChannelState( ChannelType channelType ) { - mThreadPoolManager = manager; mChannelType = channelType; } @@ -126,7 +125,7 @@ public class ChannelState extends Module implements ICallEventProvider, } @Override - public void start() + public void start( ScheduledExecutorService executor ) { //Start a monitoring task to check for FADE and END event timeouts if( mMonitoring.compareAndSet( false, true ) ) @@ -135,8 +134,8 @@ public class ChannelState extends Module implements ICallEventProvider, { try { - mStateMonitorFuture = mThreadPoolManager.scheduleFixedRate( - ThreadType.DECODER, mStateMonitor, 20, TimeUnit.MILLISECONDS ); + mStateMonitorFuture = executor.scheduleAtFixedRate( + mStateMonitor, 0, 20, TimeUnit.MILLISECONDS ); } catch( RejectedExecutionException ree ) { @@ -157,7 +156,7 @@ public class ChannelState extends Module implements ICallEventProvider, { if( mStateMonitorFuture != null ) { - boolean success = mThreadPoolManager.cancel( mStateMonitorFuture ); + boolean success = mStateMonitorFuture.cancel( true ); if( !success ) { @@ -175,7 +174,6 @@ public class ChannelState extends Module implements ICallEventProvider, mChangedAttributeListener = null; mDecoderStateListener = null; mSquelchStateListener = null; - mThreadPoolManager = null; mStateMonitor = null; } diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index ec64ab84..5455d7d5 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledExecutorService; import module.Module; import module.ProcessingChain; @@ -86,13 +87,21 @@ public class TrafficChannelManager extends Module mSystem = system; mSite = site; mAliasListName = aliasListName; -// mTrafficChannelTimeout = trafficChannelTimeout; mTrafficChannelPoolMaximumSize = trafficChannelPoolSize; } @Override public void dispose() { + for( Channel trafficChannel: mTrafficChannelPool ) + { + mChannelModel.broadcast( new ChannelEvent( trafficChannel, Event.REQUEST_DISABLE ) ); + } + + mTrafficChannelPool.clear(); + + mTrafficChannelsInUse.clear(); + mCallEventListener = null; mDecodeConfiguration = null; mPreviousDoNotMonitorCallEvent = null; @@ -350,7 +359,7 @@ public class TrafficChannelManager extends Module } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/decode/tait/Tait1200Decoder.java b/src/module/decode/tait/Tait1200Decoder.java index 9f19f99a..97b9b788 100644 --- a/src/module/decode/tait/Tait1200Decoder.java +++ b/src/module/decode/tait/Tait1200Decoder.java @@ -26,6 +26,7 @@ import instrument.tap.stream.FloatTap; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledExecutorService; import module.decode.Decoder; import module.decode.DecoderType; @@ -198,7 +199,7 @@ public class Tait1200Decoder extends Decoder implements IFilteredRealBufferListe } @Override - public void start() + public void start( ScheduledExecutorService executor ) { // TODO Auto-generated method stub diff --git a/src/module/decode/tait/Tait1200DecoderState.java b/src/module/decode/tait/Tait1200DecoderState.java index 589e9d5f..569c6a36 100644 --- a/src/module/decode/tait/Tait1200DecoderState.java +++ b/src/module/decode/tait/Tait1200DecoderState.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.TreeSet; +import java.util.concurrent.ScheduledExecutorService; import org.jdesktop.swingx.mapviewer.GeoPosition; @@ -60,7 +61,7 @@ public class Tait1200DecoderState extends DecoderState } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/demodulate/am/AMDemodulatorModule.java b/src/module/demodulate/am/AMDemodulatorModule.java index 8e311102..374491aa 100644 --- a/src/module/demodulate/am/AMDemodulatorModule.java +++ b/src/module/demodulate/am/AMDemodulatorModule.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.demodulate.am; +import java.util.concurrent.ScheduledExecutorService; + import module.Module; import sample.Listener; import sample.complex.ComplexBuffer; @@ -111,7 +113,7 @@ public class AMDemodulatorModule extends Module } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java index c6535268..7920d9a1 100644 --- a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java +++ b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.demodulate.audio; +import java.util.concurrent.ScheduledExecutorService; + import module.Module; import org.slf4j.Logger; @@ -84,7 +86,7 @@ public class DemodulatedAudioFilterModule extends Module } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java index da39a197..f96f9528 100644 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -17,6 +17,8 @@ ******************************************************************************/ package module.demodulate.fm; +import java.util.concurrent.ScheduledExecutorService; + import module.Module; import org.slf4j.Logger; @@ -97,7 +99,7 @@ public class FMDemodulatorModule extends Module } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/module/log/CallEventLogger.java b/src/module/log/CallEventLogger.java index 3d01e45b..a3e5ab14 100644 --- a/src/module/log/CallEventLogger.java +++ b/src/module/log/CallEventLogger.java @@ -58,6 +58,5 @@ public class CallEventLogger extends EventLogger implements ICallEventListener, @Override public void reset() { - super.start(); } } diff --git a/src/module/log/EventLogger.java b/src/module/log/EventLogger.java index b1d5e46f..120ee29a 100644 --- a/src/module/log/EventLogger.java +++ b/src/module/log/EventLogger.java @@ -24,6 +24,7 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.file.Path; +import java.util.concurrent.ScheduledExecutorService; import module.Module; @@ -67,7 +68,8 @@ public abstract class EventLogger extends Module public abstract String getHeader(); - public void start() + @Override + public void start( ScheduledExecutorService executor ) { if( mLogFile == null ) { diff --git a/src/module/log/MessageEventLogger.java b/src/module/log/MessageEventLogger.java index 65c1c918..de021df7 100644 --- a/src/module/log/MessageEventLogger.java +++ b/src/module/log/MessageEventLogger.java @@ -58,7 +58,6 @@ public class MessageEventLogger extends EventLogger implements @Override public void reset() { - super.start(); } @Override diff --git a/src/record/RecorderManager.java b/src/record/RecorderManager.java index b4bf4aad..4cc30ab2 100644 --- a/src/record/RecorderManager.java +++ b/src/record/RecorderManager.java @@ -86,10 +86,9 @@ public class RecorderManager implements Listener try { RealBufferWaveRecorder recorder = - new RealBufferWaveRecorder( mThreadPoolManager, - AUDIO_SAMPLE_RATE, filePrefix ); + new RealBufferWaveRecorder( AUDIO_SAMPLE_RATE, filePrefix ); - recorder.start(); + recorder.start( mThreadPoolManager.getScheduledExecutorService() ); recorder.receive( audioPacket.getAudioBuffer() ); mRecorders.put( identifier, recorder ); @@ -177,7 +176,6 @@ public class RecorderManager implements Listener sb.append( channelName ); sb.append( "_baseband" ); - return new ComplexBufferWaveRecorder( mThreadPoolManager, - AUDIO_SAMPLE_RATE, sb.toString() ); + return new ComplexBufferWaveRecorder( AUDIO_SAMPLE_RATE, sb.toString() ); } } diff --git a/src/record/wave/ComplexBufferWaveRecorder.java b/src/record/wave/ComplexBufferWaveRecorder.java index 53eed8cb..f738bc7f 100644 --- a/src/record/wave/ComplexBufferWaveRecorder.java +++ b/src/record/wave/ComplexBufferWaveRecorder.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -38,8 +39,6 @@ import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.IComplexBufferListener; import util.TimeStamp; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; /** * WAVE audio recorder module for recording complex (I&Q) samples to a wave file @@ -55,18 +54,14 @@ public class ComplexBufferWaveRecorder extends Module private Path mFile; private AudioFormat mAudioFormat; - private ThreadPoolManager mThreadPoolManager; private BufferProcessor mBufferProcessor; private ScheduledFuture mProcessorHandle; private LinkedBlockingQueue mBuffers = new LinkedBlockingQueue<>( 500 ); private AtomicBoolean mRunning = new AtomicBoolean(); - public ComplexBufferWaveRecorder( ThreadPoolManager threadPoolManager, - int sampleRate, String filePrefix ) + public ComplexBufferWaveRecorder( int sampleRate, String filePrefix ) { - mThreadPoolManager = threadPoolManager; - mAudioFormat = new AudioFormat( sampleRate, //SampleRate 16, //Sample Size 2, //Channels @@ -81,7 +76,7 @@ public class ComplexBufferWaveRecorder extends Module return mFile; } - public void start() + public void start( ScheduledExecutorService executor ) { if( mRunning.compareAndSet( false, true ) ) { @@ -103,9 +98,8 @@ public class ComplexBufferWaveRecorder extends Module mWriter = new WaveWriter( mAudioFormat, mFile ); /* Schedule the processor to run every 500 milliseconds */ - mProcessorHandle = mThreadPoolManager.scheduleFixedRate( - ThreadType.BASEBAND_RECORDING, mBufferProcessor, 500, - TimeUnit.MILLISECONDS ); + mProcessorHandle = executor.scheduleAtFixedRate( + mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS ); } catch( IOException io ) { @@ -149,8 +143,6 @@ public class ComplexBufferWaveRecorder extends Module public void dispose() { stop(); - - mThreadPoolManager = null; } @Override @@ -190,7 +182,10 @@ public class ComplexBufferWaveRecorder extends Module mFile = null; - mThreadPoolManager.cancel( mProcessorHandle ); + if( mProcessorHandle != null ) + { + mProcessorHandle.cancel( true ); + } mProcessorHandle = null; } diff --git a/src/record/wave/RealBufferWaveRecorder.java b/src/record/wave/RealBufferWaveRecorder.java index 0ad0d4f7..ac17988a 100644 --- a/src/record/wave/RealBufferWaveRecorder.java +++ b/src/record/wave/RealBufferWaveRecorder.java @@ -21,6 +21,7 @@ import java.io.IOException; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; @@ -37,8 +38,6 @@ import sample.Listener; import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import util.TimeStamp; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; /** * WAVE audio recorder module for recording real sample buffers to a wave file @@ -54,7 +53,6 @@ public class RealBufferWaveRecorder extends Module private Path mFile; private AudioFormat mAudioFormat; - private ThreadPoolManager mThreadPoolManager; private BufferProcessor mBufferProcessor; private ScheduledFuture mProcessorHandle; private LinkedBlockingQueue mBuffers = @@ -63,11 +61,8 @@ public class RealBufferWaveRecorder extends Module private AtomicBoolean mRunning = new AtomicBoolean(); - public RealBufferWaveRecorder( ThreadPoolManager threadPoolManager, - int sampleRate, String filePrefix ) + public RealBufferWaveRecorder( int sampleRate, String filePrefix ) { - mThreadPoolManager = threadPoolManager; - mAudioFormat = new AudioFormat( sampleRate, //SampleRate 16, //Sample Size 1, //Channels @@ -90,7 +85,7 @@ public class RealBufferWaveRecorder extends Module return mFile; } - public void start() + public void start( ScheduledExecutorService executor ) { if( mRunning.compareAndSet( false, true ) ) { @@ -112,9 +107,8 @@ public class RealBufferWaveRecorder extends Module mWriter = new WaveWriter( mAudioFormat, mFile ); /* Schedule the processor to run every 500 milliseconds */ - mProcessorHandle = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_RECORDING, mBufferProcessor, 500, - TimeUnit.MILLISECONDS ); + mProcessorHandle = executor.scheduleAtFixedRate( + mBufferProcessor, 0, 500, TimeUnit.MILLISECONDS ); } catch( IOException io ) { @@ -160,8 +154,6 @@ public class RealBufferWaveRecorder extends Module public void dispose() { stop(); - - mThreadPoolManager = null; } @Override @@ -198,12 +190,14 @@ public class RealBufferWaveRecorder extends Module } } - mThreadPoolManager.cancel( mProcessorHandle ); + if( mProcessorHandle != null ) + { + mProcessorHandle.cancel( true ); + } + mProcessorHandle = null; mWriter = null; mFile = null; - mProcessorHandle = null; - mThreadPoolManager = null; } else { diff --git a/src/source/mixer/RealMixerSource.java b/src/source/mixer/RealMixerSource.java index 240e55bb..7ea8dd7b 100644 --- a/src/source/mixer/RealMixerSource.java +++ b/src/source/mixer/RealMixerSource.java @@ -19,6 +19,7 @@ package source.mixer; import java.util.Iterator; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import javax.sound.sampled.AudioFormat; @@ -29,12 +30,12 @@ import javax.sound.sampled.TargetDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import dsp.gain.AutomaticGainControl; import sample.Listener; import sample.adapter.ISampleAdapter; import sample.real.RealBuffer; import source.RealSource; import source.SourceException; +import dsp.gain.AutomaticGainControl; public class RealMixerSource extends RealSource { @@ -86,7 +87,7 @@ public class RealMixerSource extends RealSource } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index f75e95e7..fada5575 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -19,7 +19,6 @@ package source.tuner; import java.util.ArrayList; import java.util.List; -import java.util.concurrent.Executors; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; @@ -39,8 +38,6 @@ import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; -import controller.NamingThreadFactory; -import controller.ThreadPoolManager.ThreadType; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.cic.ComplexPrimeCICDecimate; @@ -63,7 +60,6 @@ public class TunerChannelSource extends ComplexSource private ComplexPrimeCICDecimate mDecimationFilter; private Listener mListener; private IFrequencyChangeProcessor mFrequencyChangeProcessor; - private ScheduledExecutorService mScheduledExecutorService; private ScheduledFuture mTaskHandle; private long mTunerFrequency = 0; @@ -114,14 +110,7 @@ public class TunerChannelSource extends ComplexSource } - @Override - public void reset() - { - } - - - @Override - public void start() + public void start( ScheduledExecutorService executor ) { if( mExpended ) { @@ -132,12 +121,9 @@ public class TunerChannelSource extends ComplexSource if( mRunning.compareAndSet( false, true )) { - mScheduledExecutorService = Executors.newScheduledThreadPool( 1, - new NamingThreadFactory( "tuner channel source" ) ); - /* Schedule the decimation task to run every 10 ms (100 iterations/second) */ - mTaskHandle = mScheduledExecutorService.scheduleAtFixedRate( - mDecimationProcessor, 0, 10, TimeUnit.MILLISECONDS ); + mTaskHandle = executor.scheduleAtFixedRate( mDecimationProcessor, + 0, 10, TimeUnit.MILLISECONDS ); /* Finally, register to receive samples from the tuner */ mTuner.addListener( (Listener)this ); @@ -149,6 +135,10 @@ public class TunerChannelSource extends ComplexSource } } + @Override + public void reset() + { + } @Override public void stop() @@ -158,15 +148,10 @@ public class TunerChannelSource extends ComplexSource mTuner.releaseChannel( this ); mDecimationProcessor.shutdown(); - if( mScheduledExecutorService != null && mTaskHandle != null ) + if( mTaskHandle != null ) { - if( mTaskHandle != null ) - { - mTaskHandle.cancel( true ); - mTaskHandle = null; - } - - mScheduledExecutorService.shutdownNow(); + mTaskHandle.cancel( true ); + mTaskHandle = null; } mBuffer.clear(); @@ -349,7 +334,7 @@ public class TunerChannelSource extends ComplexSource { long start = System.currentTimeMillis(); - mBuffer.drainTo( mSampleBuffers ); + mBuffer.drainTo( mSampleBuffers, 8 ); for( Buffer buffer: mSampleBuffers ) { diff --git a/src/source/tuner/frequency/DCTrackingFrequencyControl.java b/src/source/tuner/frequency/DCTrackingFrequencyControl.java index 17704dfd..3c1cfe68 100644 --- a/src/source/tuner/frequency/DCTrackingFrequencyControl.java +++ b/src/source/tuner/frequency/DCTrackingFrequencyControl.java @@ -18,6 +18,8 @@ package source.tuner.frequency; * along with this program. If not, see ******************************************************************************/ +import java.util.concurrent.ScheduledExecutorService; + import module.Module; import org.slf4j.Logger; @@ -118,7 +120,7 @@ public class DCTrackingFrequencyControl extends Module } @Override - public void start() + public void start( ScheduledExecutorService executor ) { } diff --git a/src/source/wave/ComplexWaveSource.java b/src/source/wave/ComplexWaveSource.java index 9e4a65a8..efa8b14b 100644 --- a/src/source/wave/ComplexWaveSource.java +++ b/src/source/wave/ComplexWaveSource.java @@ -20,6 +20,7 @@ package source.wave; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -58,11 +59,11 @@ public class ComplexWaveSource extends ComplexSource implements IControllableFil public void reset() { stop(); - start(); + start( null ); } @Override - public void start() + public void start( ScheduledExecutorService executor ) { try { diff --git a/src/source/wave/RealWaveSource.java b/src/source/wave/RealWaveSource.java index 9af020c1..06e4fc25 100644 --- a/src/source/wave/RealWaveSource.java +++ b/src/source/wave/RealWaveSource.java @@ -20,6 +20,7 @@ package source.wave; import java.io.File; import java.io.IOException; import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; @@ -60,12 +61,12 @@ public class RealWaveSource extends RealSource public void reset() { stop(); - start(); + start( null ); } @Override - public void start() + public void start( ScheduledExecutorService executor ) { try { From 5573b9a9fba4e140ac3fb829983156d1fc3ee5f4 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 16:01:06 -0500 Subject: [PATCH 20/74] Changes audio timeout from 2 seconds to 1 --- src/audio/AudioManager.java | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index a8aae418..a8830c57 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -1,8 +1,6 @@ -package audio; - /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,6 +15,7 @@ package audio; * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ +package audio; import java.util.ArrayList; import java.util.Collections; @@ -51,7 +50,7 @@ public class AudioManager implements Listener, IAudioController { private static final Logger mLog = LoggerFactory.getLogger( AudioManager.class ); - public static final int AUDIO_TIMEOUT = 2000; //2 seconds + public static final int AUDIO_TIMEOUT = 1000; //1 second public static final String AUDIO_CHANNELS_PROPERTY = "audio.manager.channels"; public static final String AUDIO_MIXER_PROPERTY = "audio.manager.mixer"; @@ -433,7 +432,6 @@ public class AudioManager implements Listener, IAudioController */ public class AudioOutputConnection implements Listener { - private static final long CALL_END_DELAY = 2000; //2 seconds private static final int DISCONNECTED = -1; private AudioOutput mAudioOutput; @@ -548,7 +546,7 @@ public class AudioManager implements Listener, IAudioController public boolean isInactive() { return mLastActivity != Long.MAX_VALUE && - mLastActivity + CALL_END_DELAY < System.currentTimeMillis(); + mLastActivity + AUDIO_TIMEOUT < System.currentTimeMillis(); } public int getPriority() From 691e4c33836009a0245e493d87560d2a44c8a7c9 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 20:11:53 -0500 Subject: [PATCH 21/74] merges master commit 94d85f6 to persist FFT size. --- src/spectrum/DFTProcessor.java | 17 ++++++++++++ src/spectrum/SpectralDisplayPanel.java | 37 ++++++++++++++++++++++++++ src/spectrum/WaterfallPanel.java | 2 ++ 3 files changed, 56 insertions(+) diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 7e844301..c3761f66 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -30,6 +30,7 @@ import org.jtransforms.fft.FloatFFT_1D; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import properties.SystemProperties; import sample.Buffer; import sample.Listener; import sample.SampleType; @@ -61,6 +62,8 @@ public class DFTProcessor implements Listener, private ScheduledExecutorService mScheduler = Executors .newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); + public static final String FRAME_RATE_PROPERTY = "spectral.display.frame.rate"; + private DFTSize mDFTSize = DFTSize.FFT04096; private DFTSize mNewDFTSize = DFTSize.FFT04096; @@ -90,6 +93,15 @@ public class DFTProcessor implements Listener, { setSampleType( sampleType ); setFrameRate( 20 ); + + loadSettings(); + } + + private void loadSettings() + { + int frameRate = SystemProperties.getInstance().get( FRAME_RATE_PROPERTY, 20 ); + + setFrameRate( frameRate ); } public void dispose() @@ -170,6 +182,11 @@ public class DFTProcessor implements Listener, } mFrameRate = framesPerSecond; + + SystemProperties properties = SystemProperties.getInstance(); + + properties.set( FRAME_RATE_PROPERTY, String.valueOf( mFrameRate ) ); + properties.save(); calculateConsumptionRate(); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 9d76b912..8c3cc6ae 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -54,6 +54,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import playlist.PlaylistManager; +import properties.SystemProperties; import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; @@ -99,6 +100,7 @@ public class SpectralDisplayPanel extends JPanel private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" ); + public static final String FFT_SIZE_PROPERTY = "spectral.display.dft.size"; public static final int NO_ZOOM = 0; public static final int MAX_ZOOM = 6; @@ -155,6 +157,36 @@ public class SpectralDisplayPanel extends JPanel } init(); + + loadSettings(); + } + + private void loadSettings() + { + SystemProperties properties = SystemProperties.getInstance(); + + String rawSize = properties.get( FFT_SIZE_PROPERTY, DFTSize.FFT04096.name() ); + + DFTSize size = null; + + if( rawSize != null ) + { + try + { + size = DFTSize.valueOf( rawSize ); + } + catch( Exception e ) + { + //Do nothing + } + } + + if( size == null ) + { + size = DFTSize.FFT04096; + } + + setDFTSize( size ); } public void dispose() @@ -199,6 +231,11 @@ public class SpectralDisplayPanel extends JPanel mDFTProcessor.setDFTSize( size ); mOverlayPanel.setDFTSize( size ); mDFTSize = size; + + SystemProperties properties = SystemProperties.getInstance(); + + properties.set( FFT_SIZE_PROPERTY, size.name() ); + properties.save(); setZoom( 0, 0, 0 ); } diff --git a/src/spectrum/WaterfallPanel.java b/src/spectrum/WaterfallPanel.java index 2aa7e37a..336e8c6c 100644 --- a/src/spectrum/WaterfallPanel.java +++ b/src/spectrum/WaterfallPanel.java @@ -121,6 +121,8 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, mMemoryImageSource.setAnimated( true ); mWaterfallImage = createImage( mMemoryImageSource ); + + repaint(); } /** From c8328b3efff8372f11e40deb7f2ce339e222caa2 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 20:12:23 -0500 Subject: [PATCH 22/74] Channel editor and sub editors updates. --- src/controller/channel/ChannelEditor.java | 2 +- src/module/decode/AuxDecodeComponentEditor.java | 5 ++++- src/module/log/EventLogComponentEditor.java | 9 ++++++--- src/record/RecordComponentEditor.java | 9 ++++++--- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 2e77fcab..8836a2da 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -52,7 +52,7 @@ public class ChannelEditor extends JPanel private final static Logger mLog = LoggerFactory.getLogger( ChannelEditor.class ); - private ChannelConfigurationEditor mNameConfigurationEditor; + private NameConfigurationEditor mNameConfigurationEditor; private DecodeComponentEditor mDecodeEditor; private AuxDecodeComponentEditor mAuxDecodeEditor; private EventLogComponentEditor mEventLogEditor; diff --git a/src/module/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeComponentEditor.java index 46ba84fb..62e3c292 100644 --- a/src/module/decode/AuxDecodeComponentEditor.java +++ b/src/module/decode/AuxDecodeComponentEditor.java @@ -23,6 +23,7 @@ import java.util.List; import javax.swing.JCheckBox; +import net.miginfocom.swing.MigLayout; import module.decode.config.AuxDecodeConfiguration; import controller.channel.Channel; import controller.channel.ChannelConfigurationEditor; @@ -38,6 +39,8 @@ public class AuxDecodeComponentEditor extends ChannelConfigurationEditor public AuxDecodeComponentEditor() { + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][][][][grow]" ) ); + List decoders = DecoderType.getAuxDecoders(); Collections.sort( decoders ); @@ -46,7 +49,7 @@ public class AuxDecodeComponentEditor extends ChannelConfigurationEditor { AuxDecoderCheckBox control = new AuxDecoderCheckBox( decoder ); - add( control, "wrap" ); + add( control ); mControls.add( control ); } diff --git a/src/module/log/EventLogComponentEditor.java b/src/module/log/EventLogComponentEditor.java index f746b83d..07b7625e 100644 --- a/src/module/log/EventLogComponentEditor.java +++ b/src/module/log/EventLogComponentEditor.java @@ -21,6 +21,7 @@ import java.util.List; import javax.swing.JCheckBox; +import net.miginfocom.swing.MigLayout; import module.log.config.EventLogConfiguration; import controller.channel.Channel; import controller.channel.ChannelConfigurationEditor; @@ -38,9 +39,11 @@ public class EventLogComponentEditor extends ChannelConfigurationEditor public EventLogComponentEditor() { - add( mBinaryLogger, "span" ); - add( mDecodedLogger, "span" ); - add( mCallEventLogger, "span" ); + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][][][grow]" ) ); + + add( mBinaryLogger ); + add( mDecodedLogger ); + add( mCallEventLogger ); } /** diff --git a/src/record/RecordComponentEditor.java b/src/record/RecordComponentEditor.java index 5873534d..8b12c08a 100644 --- a/src/record/RecordComponentEditor.java +++ b/src/record/RecordComponentEditor.java @@ -21,6 +21,7 @@ import java.util.List; import javax.swing.JCheckBox; +import net.miginfocom.swing.MigLayout; import record.config.RecordConfiguration; import controller.channel.Channel; import controller.channel.ChannelConfigurationEditor; @@ -38,9 +39,11 @@ public class RecordComponentEditor extends ChannelConfigurationEditor public RecordComponentEditor() { - add( mAudioRecorder, "span" ); - add( mBasebandRecorder, "span" ); - add( mTrafficBasebandRecorder, "span" ); + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][][][grow]" ) ); + + add( mAudioRecorder ); + add( mBasebandRecorder ); + add( mTrafficBasebandRecorder ); } @Override From 5ee4a48ab38ce1e6c7f010b6440f0a3555a7d529 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 20:23:01 -0500 Subject: [PATCH 23/74] Resolved issue with duplicate properties saves --- src/module/ProcessingChain.java | 5 ----- src/properties/SystemProperties.java | 4 ++-- src/spectrum/DFTProcessor.java | 1 - src/spectrum/SpectralDisplayPanel.java | 1 - 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index e263a920..5652db05 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -493,11 +493,6 @@ public class ProcessingChain implements IChannelEventListener mLog.error( "Error starting module", e ); } } - - for( Listener listener: mUnFilteredRealBufferBroadcaster.getListeners() ) - { - mLog.debug( "*** Listener:" + listener.getClass() ); - } } else { diff --git a/src/properties/SystemProperties.java b/src/properties/SystemProperties.java index bf4b33f4..38837ed5 100644 --- a/src/properties/SystemProperties.java +++ b/src/properties/SystemProperties.java @@ -87,8 +87,8 @@ public class SystemProperties mProperties.store( out, comments ); - mLog.info( "SystemProperties - saved current properties [" + - propsPath.toString() + "]" ); +// mLog.info( "SystemProperties - saved current properties [" + +// propsPath.toString() + "]" ); } catch( Exception e ) { diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index c3761f66..7900626f 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -186,7 +186,6 @@ public class DFTProcessor implements Listener, SystemProperties properties = SystemProperties.getInstance(); properties.set( FRAME_RATE_PROPERTY, String.valueOf( mFrameRate ) ); - properties.save(); calculateConsumptionRate(); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 8c3cc6ae..1209236e 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -235,7 +235,6 @@ public class SpectralDisplayPanel extends JPanel SystemProperties properties = SystemProperties.getInstance(); properties.set( FFT_SIZE_PROPERTY, size.name() ); - properties.save(); setZoom( 0, 0, 0 ); } From 1687ca2c4b14554fb7eb21ab7ae45f0e190756f2 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 20:29:46 -0500 Subject: [PATCH 24/74] Updated data state default color setting --- src/settings/ColorSetting.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/settings/ColorSetting.java b/src/settings/ColorSetting.java index 6040fce4..d0cb3e1f 100644 --- a/src/settings/ColorSetting.java +++ b/src/settings/ColorSetting.java @@ -123,7 +123,7 @@ public class ColorSetting extends Setting "Control Gradient Middle", "Channel Control State Gradient Middle" ), CHANNEL_STATE_GRADIENT_TOP_DATA( Color.BLACK, "Data Gradient Top", "Channel Data State Gradient Top" ), - CHANNEL_STATE_GRADIENT_MIDDLE_DATA( Color.BLUE, + CHANNEL_STATE_GRADIENT_MIDDLE_DATA( new Color( 0xCC00CC ), "Data Gradient Middle", "Channel Data State Gradient Middle" ), CHANNEL_STATE_GRADIENT_TOP_FADE( Color.BLACK, "Fade Gradient Top", "Channel Fade State Gradient Top" ), From 62c5265bce6759a4086d709c6ed0206d1eb37b3b Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 15 Feb 2016 21:05:08 -0500 Subject: [PATCH 25/74] Updates launch script to allow clean exit on program termination. --- scripts/run_sdrtrunk_linux.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) mode change 100644 => 100755 scripts/run_sdrtrunk_linux.sh diff --git a/scripts/run_sdrtrunk_linux.sh b/scripts/run_sdrtrunk_linux.sh old mode 100644 new mode 100755 index 00355174..c95741eb --- a/scripts/run_sdrtrunk_linux.sh +++ b/scripts/run_sdrtrunk_linux.sh @@ -1,2 +1,2 @@ #!/bin/bash -java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk& \ No newline at end of file +java -XX:+UseG1GC -cp "*:libs/*" gui.SDRTrunk \ No newline at end of file From 9286fdf42d49a67cafbad3f1143e0fcac6b012f0 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 20 Feb 2016 17:42:07 -0500 Subject: [PATCH 26/74] Merges cdcc487 from master --- src/module/decode/state/ChannelState.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java index 836531a7..42dd2412 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/module/decode/state/ChannelState.java @@ -381,6 +381,8 @@ public class ChannelState extends Module implements ICallEventProvider, { broadcast( SquelchState.SQUELCH ); + broadcast( new MetadataReset() ); + mState = State.TEARDOWN; broadcast( ChangedAttribute.CHANNEL_STATE ); From d90684dbdc8119edc9d6e480a26b069cc46f28f4 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 20 Feb 2016 17:45:18 -0500 Subject: [PATCH 27/74] Merges master be7ab87 patch to audio metadata --- src/audio/metadata/AudioMetadata.java | 103 ++++++++++++++------------ src/audio/metadata/Metadata.java | 61 ++++++++++++++- 2 files changed, 115 insertions(+), 49 deletions(-) diff --git a/src/audio/metadata/AudioMetadata.java b/src/audio/metadata/AudioMetadata.java index b80cddff..ad498288 100644 --- a/src/audio/metadata/AudioMetadata.java +++ b/src/audio/metadata/AudioMetadata.java @@ -1,19 +1,6 @@ -package audio.metadata; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import sample.Listener; -import alias.Alias; -import alias.priority.Priority; - /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -28,15 +15,30 @@ import alias.priority.Priority; * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ +package audio.metadata; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import alias.Alias; +import alias.priority.Priority; + public class AudioMetadata implements Listener { protected static final Logger mLog = LoggerFactory.getLogger( AudioMetadata.class ); private int mSource; - private List mMetadata = new ArrayList<>(); + private Map mMetadata = new HashMap<>(); private boolean mUpdated = false; - /* Channel Selectect == highest priority */ + /* Channel Selected == highest priority */ private boolean mSelected = false; /* Call Priority */ @@ -92,7 +94,8 @@ public class AudioMetadata implements Listener copy.mPriority = mPriority; copy.mSelected = mSelected; copy.mRecordable = mRecordable; - copy.mMetadata.addAll( mMetadata ); + + copy.mMetadata.putAll( mMetadata ); copy.mUpdated = mUpdated; copy.mIdentifier = new String( mIdentifier ); @@ -106,27 +109,36 @@ public class AudioMetadata implements Listener */ public void reset() { - Iterator it = mMetadata.iterator(); - - while( it.hasNext() ) - { - if( it.next().isTemporal() ) - { - it.remove(); - } - } - /* Reset recordable state, identifier and audio call priority levels * and reprocess the metadata */ mRecordable = mSourceRecordable; setIdentifier( mSource ); mPriority = Priority.DEFAULT_PRIORITY; - for( Metadata metadata: mMetadata ) + List toRemove = new ArrayList<>(); + + for( MetadataType type: mMetadata.keySet() ) { - processMetadata( metadata ); + Metadata metadata = mMetadata.get( type ); + + if( metadata != null ) + { + if( metadata.isTemporal() ) + { + toRemove.add( type ); + } + else + { + processMetadata( metadata ); + } + } } + for( MetadataType type: toRemove ) + { + mMetadata.remove( type ); + } + mUpdated = true; } @@ -185,8 +197,14 @@ public class AudioMetadata implements Listener } else { - mMetadata.add( metadata ); - processMetadata( metadata ); + Metadata existing = mMetadata.get( metadata.getMetadataType() ); + + if( existing == null || !existing.equals( metadata ) ) + { + mMetadata.put( metadata.getMetadataType(), metadata ); + processMetadata( metadata ); + setUpdated( true ); + } } mUpdated = true; @@ -195,26 +213,17 @@ public class AudioMetadata implements Listener /** * Returns the complete set of metadata */ - public List getMetadata() + public Map getMetadata() { return mMetadata; } /** - * Returns the first metadata object matching the type, or null if the - * metadata type is not found. + * Returns the metadata object matching the type or null */ public Metadata getMetadata( MetadataType type ) { - for( Metadata metadata: mMetadata ) - { - if( metadata.getMetadataType() == type ) - { - return metadata; - } - } - - return null; + return mMetadata.get( type ); } /** @@ -296,14 +305,14 @@ public class AudioMetadata implements Listener sb.append( getPriority() ); sb.append( " updated:" + isUpdated() ); sb.append( " " ); - for( Metadata m: getMetadata() ) + for( Entry entry: getMetadata().entrySet() ) { - sb.append( m.getKey() ); + sb.append( entry.getValue().getKey() ); sb.append( ":" ); - sb.append( m.getValue() ); + sb.append( entry.getValue().getValue() ); sb.append( " " ); } return sb.toString(); } -} +} \ No newline at end of file diff --git a/src/audio/metadata/Metadata.java b/src/audio/metadata/Metadata.java index c4f41442..9d58c63c 100644 --- a/src/audio/metadata/Metadata.java +++ b/src/audio/metadata/Metadata.java @@ -1,3 +1,20 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package audio.metadata; import alias.Alias; @@ -21,7 +38,7 @@ public class Metadata mValueAlias = alias; mTemporal = temporal; } - + /** * Constucts a metadata object with the temporal settings. Temporal * indicates that this metadata will be removed at the end of a reset @@ -103,4 +120,44 @@ public class Metadata return sb.toString(); } -} + + public boolean equals( Metadata other ) + { + if( mMetadataType != other.getMetadataType() ) + { + return false; + } + + if( mTemporal != other.isTemporal() ) + { + return false; + } + + if( mValue != null && other.getValue() != null && + !mValue.contentEquals( other.getValue() )) + { + return false; + } + + if( ( mValue == null && other.getValue() != null ) || + ( mValue != null && other.getValue() == null ) ) + { + return false; + } + + if( mValueAlias != null && other.getAlias() != null && + !mValueAlias.getName().contentEquals( other.getAlias().getName() )) + { + return false; + } + + if( ( mValueAlias == null && other.getAlias() != null ) || + ( mValueAlias != null && other.getAlias() == null ) ) + { + return false; + } + + return true; + } + +} \ No newline at end of file From 7da541ed06d39254fc6e647f8990dfb802bcebaf Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 20 Feb 2016 17:46:38 -0500 Subject: [PATCH 28/74] Merges master a7da60c - TDU message text --- .../decode/p25/message/tdu/TDUMessage.java | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/module/decode/p25/message/tdu/TDUMessage.java b/src/module/decode/p25/message/tdu/TDUMessage.java index 0b41292f..85a533ac 100644 --- a/src/module/decode/p25/message/tdu/TDUMessage.java +++ b/src/module/decode/p25/message/tdu/TDUMessage.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ + package module.decode.p25.message.tdu; import module.decode.p25.message.P25Message; @@ -18,4 +36,19 @@ public class TDUMessage extends P25Message mCRC = new CRC[ 1 ]; mCRC[ 0 ] = CRC.PASSED; } + + @Override + public String getMessage() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "NAC:" ); + sb.append( getNAC() ); + sb.append( " " ); + sb.append( getDUID().getLabel() ); + + sb.append( " TERMINATOR DATA UNIT" ); + + return sb.toString(); + } } From 7ff9e9a7cea89a4d6fc424ae179d53d116dc8d28 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 20 Feb 2016 17:54:35 -0500 Subject: [PATCH 29/74] Merges master 894ebeb - patch to audio playback where audio output channels were locking up after a period of time --- src/audio/AudioManager.java | 33 +- src/audio/AudioPacket.java | 5 + src/audio/output/AudioOutput.java | 390 ++++++++++++++++++++++-- src/audio/output/MonoAudioOutput.java | 294 +++--------------- src/audio/output/StereoAudioOutput.java | 324 +++----------------- 5 files changed, 465 insertions(+), 581 deletions(-) diff --git a/src/audio/AudioManager.java b/src/audio/AudioManager.java index a8830c57..7b9a8425 100644 --- a/src/audio/AudioManager.java +++ b/src/audio/AudioManager.java @@ -430,19 +430,17 @@ public class AudioManager implements Listener, IAudioController * output and maintains current state information about the audio activity * received form the source. */ - public class AudioOutputConnection implements Listener + public class AudioOutputConnection { private static final int DISCONNECTED = -1; private AudioOutput mAudioOutput; private int mPriority = 0; - private long mLastActivity = System.currentTimeMillis(); private int mSource = DISCONNECTED; public AudioOutputConnection( AudioOutput audioOutput ) { mAudioOutput = audioOutput; - mAudioOutput.addAudioEventListener( this ); } public void receive( AudioPacket packet ) @@ -458,8 +456,6 @@ public class AudioManager implements Listener, IAudioController updateLowestPriorityAssignment(); } - updateTimestamp(); - if( mAudioOutput != null ) { mAudioOutput.receive( packet ); @@ -482,11 +478,6 @@ public class AudioManager implements Listener, IAudioController } } - private void updateTimestamp() - { - mLastActivity = System.currentTimeMillis(); - } - /** * Terminates the audio output and prepares this connection for disposal */ @@ -519,7 +510,8 @@ public class AudioManager implements Listener, IAudioController public void connect( int source ) { mSource = source; - updateTimestamp(); + + mAudioOutput.updateTimestamp(); } /** @@ -537,6 +529,7 @@ public class AudioManager implements Listener, IAudioController public void disconnect() { mSource = DISCONNECTED; + mPriority = 0; } /** @@ -545,28 +538,12 @@ public class AudioManager implements Listener, IAudioController */ public boolean isInactive() { - return mLastActivity != Long.MAX_VALUE && - mLastActivity + AUDIO_TIMEOUT < System.currentTimeMillis(); + return ( mAudioOutput.getLastActivityTimestamp() + AUDIO_TIMEOUT ) < System.currentTimeMillis(); } public int getPriority() { return mPriority; } - - @Override - public void receive( AudioEvent event ) - { - switch( event.getType() ) - { - case AUDIO_STARTED: - case AUDIO_STOPPED: - case AUDIO_CONTINUATION: - updateTimestamp(); - break; - default: - break; - } - } } } \ No newline at end of file diff --git a/src/audio/AudioPacket.java b/src/audio/AudioPacket.java index c14754e1..12bbf22b 100644 --- a/src/audio/AudioPacket.java +++ b/src/audio/AudioPacket.java @@ -64,6 +64,11 @@ public class AudioPacket return mAudioData; } + public boolean hasAudioBuffer() + { + return mAudioData != null; + } + public enum Type { AUDIO, diff --git a/src/audio/output/AudioOutput.java b/src/audio/output/AudioOutput.java index 31f4300f..8d5d22b3 100644 --- a/src/audio/output/AudioOutput.java +++ b/src/audio/output/AudioOutput.java @@ -1,34 +1,165 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package audio.output; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.BooleanControl; +import javax.sound.sampled.Control; import javax.sound.sampled.FloatControl; +import javax.sound.sampled.Line; +import javax.sound.sampled.LineEvent; import javax.sound.sampled.LineListener; +import javax.sound.sampled.LineUnavailableException; +import javax.sound.sampled.Mixer; +import javax.sound.sampled.SourceDataLine; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Broadcaster; import sample.Listener; +import source.mixer.MixerChannel; import audio.AudioEvent; import audio.AudioPacket; +import audio.AudioPacket.Type; import audio.metadata.AudioMetadata; import controller.ThreadPoolManager; +import controller.ThreadPoolManager.ThreadType; public abstract class AudioOutput implements Listener, LineListener { private final static Logger mLog = LoggerFactory.getLogger( AudioOutput.class ); - private Listener mAudioMetadataListener; - protected Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); - protected LinkedTransferQueue mBuffer = new LinkedTransferQueue<>(); - protected ThreadPoolManager mThreadPoolManager; - protected boolean mCanProcessAudio; - protected long mLastActivity; + private LinkedTransferQueue mBuffer = new LinkedTransferQueue<>(); + private int mBufferStartThreshold; + private int mBufferStopThreshold; - public AudioOutput( ThreadPoolManager threadPoolManager ) + private Listener mAudioMetadataListener; + private Broadcaster mAudioEventBroadcaster = new Broadcaster<>(); + + private ThreadPoolManager mThreadPoolManager; + private ScheduledFuture mProcessorTask; + + private SourceDataLine mOutput; + private Mixer mMixer; + private MixerChannel mMixerChannel; + private FloatControl mGainControl; + private BooleanControl mMuteControl; + + private AudioEvent mAudioStartEvent; + private AudioEvent mAudioStopEvent; + + private boolean mCanProcessAudio = false; + private long mLastActivity = System.currentTimeMillis(); + + /** + * Single audio channel playback with automatic starting and stopping of the + * underlying sourcedataline specified by the mixer and mixer channel + * arguments. + * + * Maintains an internal non-blocking audio packet queue and processes this + * queue 25 times a second (every 40 ms). + * + * @param threadPoolManager for assigning buffer processing schedule task + * @param mixer to obtain source data line + * @param mixerChannel either mono or left/right stereo + * @param audioFormat to use during playback + * @param lineInfo to use when obtaining the source data line + * @param requestedBufferSize of approximately 1 second of audio + */ + public AudioOutput( ThreadPoolManager threadPoolManager, + Mixer mixer, + MixerChannel mixerChannel, + AudioFormat audioFormat, + Line.Info lineInfo, + int requestedBufferSize ) { mThreadPoolManager = threadPoolManager; + mMixer = mixer; + mMixerChannel = mixerChannel; + + try + { + mOutput = (SourceDataLine)mMixer.getLine( lineInfo ); + + if( mOutput != null ) + { + mOutput.open( audioFormat, requestedBufferSize ); + + //Start threshold: buffer is full with 10% or less of capacity remaining + mBufferStartThreshold = (int)( mOutput.getBufferSize() * 0.10 ); + + //Stop threshold: buffer is empty with 90% or more capacity available + mBufferStopThreshold = (int)( mOutput.getBufferSize() * 0.90 ); + + mOutput.addLineListener( this ); + + if( mOutput != null ) + { + try + { + Control gain = mOutput.getControl( FloatControl.Type.MASTER_GAIN ); + mGainControl = (FloatControl)gain; + } + catch( IllegalArgumentException iae ) + { + mLog.warn( "Couldn't obtain MASTER GAIN control for stereo line [" + + mixer.getMixerInfo().getName() + " | " + getChannelName() + "]" ); + } + + try + { + Control mute = mOutput.getControl( BooleanControl.Type.MUTE ); + mMuteControl = (BooleanControl)mute; + } + catch( IllegalArgumentException iae ) + { + mLog.warn( "Couldn't obtain MUTE control for stereo line [" + + mixer.getMixerInfo().getName() + " | " + getChannelName() + "]" ); + } + + /* Run the queue processor task every 40 milliseconds or 25 times a second */ + mProcessorTask = mThreadPoolManager.scheduleFixedRate( + ThreadType.AUDIO_PROCESSING, new BufferProcessor(), + 40, TimeUnit.MILLISECONDS ); + } + + mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, + getChannelName() ); + mAudioStopEvent = new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, + getChannelName() ); + + mCanProcessAudio = true; + } + } + catch ( LineUnavailableException e ) + { + mLog.error( "Couldn't obtain audio source data line for " + + "audio output - mixer [" + mMixer.getMixerInfo().getName() + "]" ); + } } public void reset() @@ -41,36 +172,52 @@ public abstract class AudioOutput implements Listener, LineListener { mCanProcessAudio = false; + if( mProcessorTask != null && mThreadPoolManager != null ) + { + mThreadPoolManager.cancel( mProcessorTask ); + } + + mThreadPoolManager = null; + mProcessorTask = null; + mBuffer.clear(); mAudioEventBroadcaster.dispose(); mAudioEventBroadcaster = null; mAudioMetadataListener = null; - mThreadPoolManager = null; - } - - public abstract String getChannelName(); - - public abstract void setMuted( boolean muted ); - public abstract boolean isMuted(); - - public abstract FloatControl getGainControl(); - public abstract boolean hasGainControl(); - - protected void broadcast( AudioMetadata metadata ) - { - if( mAudioMetadataListener != null ) + if( mOutput != null ) { - mAudioMetadataListener.receive( metadata ); + mOutput.close(); } + + mOutput = null; + mGainControl = null; + mMuteControl = null; } - - protected void broadcast( AudioEvent audioEvent ) + + /** + * Converts the audio packet data into a byte buffer format appropriate for + * the underlying source data line. + */ + protected abstract ByteBuffer convert( AudioPacket packet ); + + /** + * Audio output channel name + */ + public String getChannelName() { - mAudioEventBroadcaster.broadcast( audioEvent ); + return mMixerChannel.getLabel(); } + /** + * Mixer Channel for this audio output + */ + protected MixerChannel getMixerChannel() + { + return mMixerChannel; + } + /** * Registers a single listener to receive audio start and audio stop events */ @@ -84,6 +231,14 @@ public abstract class AudioOutput implements Listener, LineListener mAudioEventBroadcaster.removeListener( listener ); } + /** + * Broadcasts an audio event to the registered listener + */ + private void broadcast( AudioEvent audioEvent ) + { + mAudioEventBroadcaster.broadcast( audioEvent ); + } + /** * Registers a single listener to receive the audio metadata from each * audio packet @@ -99,21 +254,200 @@ public abstract class AudioOutput implements Listener, LineListener } /** - * Timestamp when most recent buffer was received + * Broadcasts audio metadata to the registered listener */ - public long getLastBufferReceived() + private void broadcast( AudioMetadata metadata ) + { + if( mAudioMetadataListener != null ) + { + mAudioMetadataListener.receive( metadata ); + } + } + + /** + * Timestamp of either last buffer received or last buffer processed + */ + public long getLastActivityTimestamp() { return mLastActivity; } + /** + * Updates the last activity timestamp to current system time + */ + public void updateTimestamp() + { + mLastActivity = System.currentTimeMillis(); + } + @Override public void receive( AudioPacket packet ) { if( mCanProcessAudio ) { - mLastActivity = System.currentTimeMillis(); + //Update the activity timestamp so that this audio output doesn't + //get disconnected before it starts processing the audio stream + updateTimestamp(); mBuffer.add( packet ); } } + + public class BufferProcessor implements Runnable + { + private AtomicBoolean mProcessing = new AtomicBoolean(); + + public BufferProcessor() + { + } + + @Override + public void run() + { + try + { + /* The processing flag ensures that only one instance of the + * processor can run at any given time */ + if( mProcessing.compareAndSet( false, true ) ) + { + List packets = new ArrayList(); + + mBuffer.drainTo( packets ); + + for( AudioPacket packet: packets ) + { + if( packet.getType() == Type.AUDIO ) + { + broadcast( packet.getAudioMetadata() ); + + ByteBuffer buffer = convert( packet ); + + int wrote = 0; + + if( !mOutput.isRunning() ) + { + int toWrite = mOutput.available(); + + if( toWrite > buffer.array().length ) + { + toWrite = buffer.array().length; + } + + //Top off the buffer and check if we can start it + wrote += mOutput.write( buffer.array(), 0, toWrite ); + + checkStart(); + } + + if( mOutput.isRunning() && wrote < buffer.array().length ) + { + //Blocking write + wrote += mOutput.write( buffer.array(), wrote, + buffer.array().length - wrote ); + } + + updateTimestamp(); + } + } + + checkStop(); + + mProcessing.set( false ); + } + } + catch( Exception e ) + { + mLog.error( "Error while processing audio buffers", e ); + } + } + + /** + * Starts audio playback once audio buffer is almost full and remaining + * capacity falls below the start threshold. + */ + private void checkStart() + { + if( mCanProcessAudio && + !mOutput.isRunning() && + mOutput.available() <= mBufferStartThreshold ) + { + mOutput.start(); + } + } + + /** + * Stops audio playback and drains the audio buffer to empty when the + * audio buffer is mostly empty and the available buffer capacity + * exceeds the stop threshold + */ + private void checkStop() + { + if( mCanProcessAudio && + mOutput.isRunning() && + mOutput.available() >= mBufferStopThreshold ) + { + mOutput.drain(); + mOutput.stop(); + } + } + } + + /** + * Sets the mute state for this audio output channel + */ + public void setMuted( boolean muted ) + { + if( mMuteControl != null ) + { + mMuteControl.setValue( muted ); + + broadcast( new AudioEvent( muted ? AudioEvent.Type.AUDIO_MUTED : + AudioEvent.Type.AUDIO_UNMUTED, getChannelName() ) ); + } + } + + /** + * Current mute state for this audio output channel + */ + public boolean isMuted() + { + if( mMuteControl != null ) + { + return mMuteControl.getValue(); + } + + return false; + } + + /** + * Gain/volume control for this audio output channel, if one is available. + */ + public FloatControl getGainControl() + { + return mGainControl; + } + + public boolean hasGainControl() + { + return mGainControl != null; + } + + /** + * Monitors the source data line playback state and broadcasts audio events + * to the registered listener as the state changes + */ + @Override + public void update( LineEvent event ) + { + LineEvent.Type type = event.getType(); + + if( type == LineEvent.Type.START ) + { + mAudioEventBroadcaster.broadcast( mAudioStartEvent ); + } + else if( type == LineEvent.Type.STOP ) + { + mAudioEventBroadcaster.broadcast( mAudioStopEvent ); + } + } } \ No newline at end of file diff --git a/src/audio/output/MonoAudioOutput.java b/src/audio/output/MonoAudioOutput.java index f1aad5cd..24ab4f9d 100644 --- a/src/audio/output/MonoAudioOutput.java +++ b/src/audio/output/MonoAudioOutput.java @@ -1,282 +1,74 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package audio.output; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ShortBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import javax.sound.sampled.BooleanControl; -import javax.sound.sampled.Control; -import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineEvent; -import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; -import javax.sound.sampled.SourceDataLine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import source.mixer.MixerChannel; -import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; -import audio.AudioPacket.Type; import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; /** - * Mono Audio output with automatic flow control based on the availability of - * audio data packets. + * Mono Audio output implementation */ public class MonoAudioOutput extends AudioOutput { - private final static Logger mLog = LoggerFactory.getLogger( MonoAudioOutput.class ); - - /* Output line buffer size is set at one half second of audio at 48 kHz and - * two bytes per sample */ private final static int BUFFER_SIZE = 48000; - /* The queue processor will run every 20 milliseconds checking for inbound - * audio and automatically starting or stopping audio playback. We set the - * data line available space threshold to start when we're 20 ms away from - * being full, and the stop threshold when 20 ms away from being empty */ - private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25; - private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS; - private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS; - - private SourceDataLine mOutput; - private FloatControl mGainControl; - private BooleanControl mMuteControl; - private AudioEvent mAudioStartEvent; - private AudioEvent mAudioStopEvent; - private AudioEvent mAudioContinuationEvent; - private ScheduledFuture mProcessorTask; - public MonoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer ) { - super( threadPoolManager ); + super( threadPoolManager, + mixer, + MixerChannel.MONO, + AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, + AudioFormats.MONO_SOURCE_DATALINE_INFO, + BUFFER_SIZE ); + } - mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, - getChannelName() ); - mAudioStopEvent = new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, - getChannelName() ); - mAudioContinuationEvent = new AudioEvent( AudioEvent.Type.AUDIO_CONTINUATION, - getChannelName() ); - - try + /** + * Converts the audio packet data into mono audio frames. + */ + protected ByteBuffer convert( AudioPacket packet ) + { + if( packet.hasAudioBuffer() ) { - mOutput = (SourceDataLine)mixer.getLine( - AudioFormats.MONO_SOURCE_DATALINE_INFO ); + float[] samples = packet.getAudioBuffer().getSamples(); - if( mOutput != null ) + /* Little-endian byte buffer */ + ByteBuffer buffer = + ByteBuffer.allocate( samples.length * 2 ) + .order( ByteOrder.LITTLE_ENDIAN ); + + ShortBuffer shortBuffer = buffer.asShortBuffer(); + + for( float sample: samples ) { - mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_MONO, BUFFER_SIZE ); - mOutput.addLineListener( this ); - mCanProcessAudio = true; - } - } - catch ( LineUnavailableException e ) - { - mLog.error( "Couldn't obtain source data line for 48kHz PCM mono " - + "audio output" ); - } - - if( mOutput != null ) - { - try - { - Control gain = mOutput.getControl( FloatControl.Type.MASTER_GAIN ); - mGainControl = (FloatControl)gain; - } - catch( IllegalArgumentException iae ) - { - mLog.warn( "Couldn't obtain MASTER GAIN control for mono line [" + - mixer.getMixerInfo().getName() + "]" ); + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); } - try - { - Control mute = mOutput.getControl( BooleanControl.Type.MUTE ); - mMuteControl = (BooleanControl)mute; - } - catch( IllegalArgumentException iae ) - { - mLog.warn( "Couldn't obtain MUTE control for mono line [" + - mixer.getMixerInfo().getName() + "]" ); - } - - /* Run the queue processor task every 40 milliseconds */ - mProcessorTask = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_PROCESSING, new QueueProcessor(), - 40, TimeUnit.MILLISECONDS ); - } - } - - @Override - public String getChannelName() - { - return MixerChannel.MONO.getLabel(); - } - - public void dispose() - { - if( mProcessorTask != null && mThreadPoolManager != null ) - { - mThreadPoolManager.cancel( mProcessorTask ); + return buffer; } - mProcessorTask = null; - - super.dispose(); - - if( mOutput != null ) - { - mOutput.close(); - } - - mOutput = null; - mGainControl = null; - mMuteControl = null; - } - - public class QueueProcessor implements Runnable - { - private AtomicBoolean mProcessing = new AtomicBoolean(); - - @Override - public void run() - { - try - { - /* The mProcessing flag ensures that only one instance of the - * processor can run at any given time */ - if( mProcessing.compareAndSet( false, true ) ) - { - List packets = new ArrayList(); - - mBuffer.drainTo( packets ); - - if( packets.size() > 0 ) - { - for( AudioPacket packet: packets ) - { - broadcast( mAudioContinuationEvent ); - - if( mCanProcessAudio ) - { - if( packet.getType() == Type.AUDIO ) - { - float[] samples = packet.getAudioBuffer().getSamples(); - - /* Little-endian byte buffer */ - ByteBuffer buffer = - ByteBuffer.allocate( samples.length * 2 ) - .order( ByteOrder.LITTLE_ENDIAN ); - - ShortBuffer shortBuffer = buffer.asShortBuffer(); - - for( float sample: samples ) - { - shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); - } - - //This is a blocking write - mOutput.write( buffer.array(), 0, buffer.array().length ); - - checkStart(); - - broadcast( packet.getAudioMetadata() ); - - mLastActivity = System.currentTimeMillis(); - } - } - } - } - else - { - checkStop(); - } - - mProcessing.set( false ); - } - } - catch( Exception e ) - { - mLog.error( "Error while processing audio buffers", e ); - } - } - - private void checkStart() - { - if( mCanProcessAudio && - !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) - { - mOutput.start(); - } - } - - private void checkStop() - { - if( mCanProcessAudio && mOutput.isRunning() && - mOutput.available() >= STOP_THRESHOLD ) - { - mOutput.drain(); - mOutput.stop(); - } - } - } - - @Override - public void setMuted( boolean muted ) - { - if( mMuteControl != null ) - { - mMuteControl.setValue( muted ); - - broadcast( new AudioEvent( muted ? AudioEvent.Type.AUDIO_MUTED : - AudioEvent.Type.AUDIO_UNMUTED, getChannelName() ) ); - } - } - - @Override - public boolean isMuted() - { - if( mMuteControl != null ) - { - return mMuteControl.getValue(); - } - - return false; - } - - @Override - public FloatControl getGainControl() - { - return mGainControl; - } - - @Override - public boolean hasGainControl() - { - return mGainControl != null; - } - - @Override - public void update( LineEvent event ) - { - LineEvent.Type type = event.getType(); - - if( type == LineEvent.Type.START ) - { - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); - } - else if( type == LineEvent.Type.STOP ) - { - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); - } + return null; } } \ No newline at end of file diff --git a/src/audio/output/StereoAudioOutput.java b/src/audio/output/StereoAudioOutput.java index 8a624d18..c12adffd 100644 --- a/src/audio/output/StereoAudioOutput.java +++ b/src/audio/output/StereoAudioOutput.java @@ -1,312 +1,88 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package audio.output; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.ShortBuffer; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import javax.sound.sampled.BooleanControl; -import javax.sound.sampled.Control; -import javax.sound.sampled.FloatControl; -import javax.sound.sampled.LineEvent; -import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.Mixer; -import javax.sound.sampled.SourceDataLine; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import source.mixer.MixerChannel; -import audio.AudioEvent; import audio.AudioFormats; import audio.AudioPacket; -import audio.AudioPacket.Type; import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; /** - * Stereo Audio output in stereo format with automatic flow control based on the - * availability of audio data packets. + * Stereo audio output implementation. */ public class StereoAudioOutput extends AudioOutput { - private final static Logger mLog = LoggerFactory.getLogger( StereoAudioOutput.class ); - - /* Output line buffer size is set at one half second of audio at 48 kHz and - * two bytes per sample for two channels */ private final static int BUFFER_SIZE = 96000; - /* The queue processor will run every 20 milliseconds checking for inbound - * audio and automatically start or stop audio playback. We set the - * data line available space threshold to start when we're 20 ms away from - * being full and the stop threshold when 20 ms away from being empty */ - private final static int SAMPLE_SIZE_40_MS = BUFFER_SIZE / 25; - private final static int START_THRESHOLD = SAMPLE_SIZE_40_MS; - private final static int STOP_THRESHOLD = BUFFER_SIZE - SAMPLE_SIZE_40_MS; - - private SourceDataLine mOutput; - private FloatControl mGainControl; - private BooleanControl mMuteControl; - private AudioEvent mAudioStartEvent; - private AudioEvent mAudioStopEvent; - private AudioEvent mAudioContinuationEvent; - private MixerChannel mMixerChannel; - private ScheduledFuture mProcessorTask; - public StereoAudioOutput( ThreadPoolManager threadPoolManager, Mixer mixer, MixerChannel channel ) { - super( threadPoolManager ); - - assert( channel == MixerChannel.LEFT || channel == MixerChannel.RIGHT ); - - mMixerChannel = channel; + super( threadPoolManager, + mixer, + channel, + AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, + AudioFormats.STEREO_SOURCE_DATALINE_INFO, + BUFFER_SIZE ); + } - mAudioStartEvent = new AudioEvent( AudioEvent.Type.AUDIO_STARTED, - getChannelName() ); - mAudioStopEvent = new AudioEvent( AudioEvent.Type.AUDIO_STOPPED, - getChannelName() ); - mAudioContinuationEvent = new AudioEvent( AudioEvent.Type.AUDIO_CONTINUATION, - getChannelName() ); - - try + /** + * Converts the audio packet data into stereo audio frames with the mixer + * channel containing the audio and the other channel containing zero + * valued (silent) samples. + */ + protected ByteBuffer convert( AudioPacket packet ) + { + if( packet.hasAudioBuffer() ) { - mOutput = (SourceDataLine)mixer.getLine( - AudioFormats.STEREO_SOURCE_DATALINE_INFO ); + float[] samples = packet.getAudioBuffer().getSamples(); - if( mOutput != null ) - { - mOutput.open( AudioFormats.PCM_SIGNED_48KHZ_16BITS_STEREO, BUFFER_SIZE ); - mOutput.addLineListener( this ); - mCanProcessAudio = true; - } - } - catch ( LineUnavailableException e ) - { - mLog.error( "Couldn't obtain source data line for 48kHz PCM stereo " - + "audio output - mixer [" + mixer.getMixerInfo().getName() + - "] channel [" + mMixerChannel.name() + "]" ); - } - - if( mOutput != null ) - { - try - { - Control gain = mOutput.getControl( FloatControl.Type.MASTER_GAIN ); - mGainControl = (FloatControl)gain; - } - catch( IllegalArgumentException iae ) - { - mLog.warn( "Couldn't obtain MASTER GAIN control for stereo line [" + - mixer.getMixerInfo().getName() + " | " + channel.name() + "]" ); - } + /* Little-endian byte buffer */ + ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 ) + .order( ByteOrder.LITTLE_ENDIAN ); - try - { - Control mute = mOutput.getControl( BooleanControl.Type.MUTE ); - mMuteControl = (BooleanControl)mute; - } - catch( IllegalArgumentException iae ) - { - mLog.warn( "Couldn't obtain MUTE control for stereo line [" + - mixer.getMixerInfo().getName() + " | " + channel.name() + "]" ); - } - - /* Run the queue processor task every 40 milliseconds */ - mProcessorTask = mThreadPoolManager.scheduleFixedRate( - ThreadType.AUDIO_PROCESSING, new QueueProcessor(), - 40, TimeUnit.MILLISECONDS ); - } - } + ShortBuffer shortBuffer = buffer.asShortBuffer(); - @Override - public String getChannelName() - { - return mMixerChannel.getLabel(); - } - - public void dispose() - { - if( mProcessorTask != null && mThreadPoolManager != null ) - { - mThreadPoolManager.cancel( mProcessorTask ); - } - - mProcessorTask = null; - - super.dispose(); - - if( mOutput != null ) - { - mOutput.close(); - } - - mOutput = null; - mGainControl = null; - mMuteControl = null; - } - - private ByteBuffer convert( float[] samples ) - { - /* Little-endian byte buffer */ - ByteBuffer buffer = ByteBuffer.allocate( samples.length * 4 ) - .order( ByteOrder.LITTLE_ENDIAN ); - - ShortBuffer shortBuffer = buffer.asShortBuffer(); - - if( mMixerChannel == MixerChannel.LEFT ) - { - for( float sample: samples ) + if( getMixerChannel() == MixerChannel.LEFT ) { - shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); - shortBuffer.put( (short)0 ); - } - } - else - { - for( float sample: samples ) - { - shortBuffer.put( (short)0 ); - shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); - } - } - - return buffer; - } - - public class QueueProcessor implements Runnable - { - private AtomicBoolean mProcessing = new AtomicBoolean(); - - public QueueProcessor() - { - } - - @Override - public void run() - { - try - { - /* The mProcessing flag ensures that only one instance of the - * processor can run at any given time */ - if( mProcessing.compareAndSet( false, true ) ) + for( float sample: samples ) { - List packets = new ArrayList(); - - mBuffer.drainTo( packets ); - - if( packets.size() > 0 ) - { - - for( AudioPacket packet: packets ) - { - broadcast( mAudioContinuationEvent ); - - if( mCanProcessAudio ) - { - if( packet.getType() == Type.AUDIO ) - { - ByteBuffer buffer = convert( - packet.getAudioBuffer().getSamples() ); - - mOutput.write( buffer.array(), 0, - buffer.array().length ); - - checkStart(); - - broadcast( packet.getAudioMetadata() ); - - mLastActivity = System.currentTimeMillis(); - } - } - } - } - else - { - checkStop(); - } - - mProcessing.set( false ); + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); + shortBuffer.put( (short)0 ); } } - catch( Exception e ) + else { - mLog.error( "Error while processing audio buffers", e ); + for( float sample: samples ) + { + shortBuffer.put( (short)0 ); + shortBuffer.put( (short)( sample * Short.MAX_VALUE ) ); + } } - } - - private void checkStart() - { - if( mCanProcessAudio && - !mOutput.isRunning() && mOutput.available() <= START_THRESHOLD ) - { - mOutput.start(); - } - } - - private void checkStop() - { - if( mCanProcessAudio && mOutput.isRunning() && - mOutput.available() >= STOP_THRESHOLD ) - { - mOutput.drain(); - mOutput.stop(); - } - } - } - - @Override - public void setMuted( boolean muted ) - { - if( mMuteControl != null ) - { - mMuteControl.setValue( muted ); - broadcast( new AudioEvent( muted ? AudioEvent.Type.AUDIO_MUTED : - AudioEvent.Type.AUDIO_UNMUTED, getChannelName() ) ); - } - } - - @Override - public boolean isMuted() - { - if( mMuteControl != null ) - { - return mMuteControl.getValue(); + return buffer; } - return false; - } - - @Override - public FloatControl getGainControl() - { - return mGainControl; - } - - @Override - public boolean hasGainControl() - { - return mGainControl != null; - } - - - @Override - public void update( LineEvent event ) - { - LineEvent.Type type = event.getType(); - - if( type == LineEvent.Type.START ) - { - mAudioEventBroadcaster.broadcast( mAudioStartEvent ); - } - else if( type == LineEvent.Type.STOP ) - { - mAudioEventBroadcaster.broadcast( mAudioStopEvent ); - } + return null; } } \ No newline at end of file From ca2b12b8685c731eae2ff76c029c30b2354b5f1f Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 20 Feb 2016 17:56:28 -0500 Subject: [PATCH 30/74] Merges master 88d3088 --- src/module/decode/p25/P25_C4FMDecoder.java | 1 - src/module/decode/state/ChannelList.java | 7 +++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index 09d6e45d..c9f8ba7f 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -77,7 +77,6 @@ public class P25_C4FMDecoder extends P25Decoder /* Filter demodulated sample buffers */ float[] filter = FilterFactory.getLowPass( 48000, 2500, 4000, 80, WindowType.HANNING, true ); -// mLog.debug( "Demod Filter tap count:" + filter.length + " coefficients:" + Arrays.toString( filter ) ); mC4FMPreFilter = new RealFIRFilter_RB_RB( filter, 1.0f ); /* Shape gain and frequency offsets to optimize sample stream */ diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index 16fe4e4e..fc333914 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -19,17 +19,15 @@ package module.decode.state; import java.awt.Color; import java.awt.Component; -import java.awt.Point; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; -import java.util.HashMap; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import javax.swing.JMenu; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JSeparator; -import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; @@ -57,7 +55,8 @@ public class ChannelList extends JPanel implements ChannelEventListener private final static Logger mLog = LoggerFactory.getLogger( ChannelList.class ); - private Map mDisplayedPanels = new HashMap(); + private Map mDisplayedPanels = + new ConcurrentHashMap(); private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; From bd09911e687961865d1f84c59f93b8bbc1bb94b9 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 21 Feb 2016 05:45:54 -0500 Subject: [PATCH 31/74] Alias updates to allow JAXB persistence --- src/alias/Alias.java | 10 ++++++++-- src/alias/AliasID.java | 23 ++++++++++++++++++++++- src/alias/action/AliasAction.java | 25 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 7def2d64..d455b2de 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -22,11 +22,15 @@ import java.util.ArrayList; import java.util.List; import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; import alias.action.AliasAction; import alias.priority.Priority; -import audio.inverted.AudioType; +@XmlSeeAlso( { AliasID.class, AliasAction.class } ) +@XmlRootElement( name = "alias" ) public class Alias { private String mList; @@ -121,6 +125,7 @@ public class Alias mIconName = iconName; } + @XmlElement( name="id" ) public List getId() { return mAliasIDs; @@ -141,6 +146,7 @@ public class Alias mAliasIDs.remove( id ); } + @XmlElement( name="action" ) public List getAction() { return mAliasActions; diff --git a/src/alias/AliasID.java b/src/alias/AliasID.java index 27b84f2d..83a6c360 100644 --- a/src/alias/AliasID.java +++ b/src/alias/AliasID.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,6 +17,27 @@ ******************************************************************************/ package alias; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import alias.id.esn.Esn; +import alias.id.fleetsync.FleetsyncID; +import alias.id.fleetsync.StatusID; +import alias.id.lojack.LoJackFunctionAndID; +import alias.id.mdc.MDC1200ID; +import alias.id.mobileID.Min; +import alias.id.mpt1327.MPT1327ID; +import alias.id.siteID.SiteID; +import alias.id.talkgroup.TalkgroupID; +import alias.id.uniqueID.UniqueID; +import alias.priority.Priority; +import alias.record.NonRecordable; + +@XmlSeeAlso( { Esn.class, FleetsyncID.class, Group.class, LoJackFunctionAndID.class, + MDC1200ID.class, Min.class, MPT1327ID.class, NonRecordable.class, + Priority.class, SiteID.class, StatusID.class, TalkgroupID.class, + UniqueID.class } ) +@XmlRootElement( name = "id" ) public abstract class AliasID { public AliasID() diff --git a/src/alias/action/AliasAction.java b/src/alias/action/AliasAction.java index 08308c82..b2d409b2 100644 --- a/src/alias/action/AliasAction.java +++ b/src/alias/action/AliasAction.java @@ -1,12 +1,37 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ package alias.action; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + import message.Message; import alias.Alias; +import alias.action.beep.BeepAction; +import alias.action.clip.ClipAction; +import alias.action.script.ScriptAction; import controller.ThreadPoolManager; /** * Alias action defines an action to execute when an alias is detected active. */ +@XmlSeeAlso( { BeepAction.class, ClipAction.class, ScriptAction.class } ) +@XmlRootElement( name = "action" ) public abstract class AliasAction { public AliasAction() From e5004f9fa6009d7b4e76186997e2b1477355eb14 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 21 Feb 2016 20:02:27 -0500 Subject: [PATCH 32/74] Work in progress - updates to alias and alias ID editors. --- src/alias/Alias.java | 2 +- src/alias/AliasEditor.java | 2 +- src/alias/AliasFactory.java | 74 ++++++- src/alias/AliasID.java | 6 +- src/alias/AliasIdentifierEditor.java | 80 +++++++- src/alias/AliasList.java | 6 +- src/alias/AliasNameEditor.java | 8 +- src/alias/AliasNode.java | 12 +- src/alias/ComponentEditor.java | 137 +++++++++++++ src/alias/ComponentValidationException.java | 14 ++ src/alias/EmptyAliasActionEditor.java | 32 +++ src/alias/EmptyAliasIDEditor.java | 31 +++ src/alias/id/esn/ESNEditor.java | 106 +++++----- src/alias/id/esn/ESNNode.java | 2 +- src/alias/id/fleetsync/FleetsyncIDEditor.java | 124 +++++++----- src/alias/id/fleetsync/FleetsyncIDNode.java | 2 +- src/alias/id/fleetsync/StatusIDEditor.java | 120 ----------- src/alias/id/lojack/LoJackIDEditor.java | 148 ++++++++------ src/alias/id/lojack/LoJackIDNode.java | 2 +- src/alias/id/mdc/MDC1200IDEditor.java | 121 ++++++----- src/alias/id/mdc/MDC1200IDNode.java | 2 +- src/alias/id/mobileID/MINEditor.java | 122 ++++++----- src/alias/id/mobileID/MINNode.java | 2 +- src/alias/id/mpt1327/MPT1327IDEditor.java | 123 ++++++----- src/alias/id/mpt1327/MPT1327IDNode.java | 2 +- .../nonrecordable}/NonRecordable.java | 8 +- .../id/nonrecordable/NonRecordableEditor.java | 62 ++++++ .../nonrecordable}/NonRecordableNode.java | 4 +- src/alias/{ => id}/priority/Priority.java | 2 +- src/alias/id/priority/PriorityEditor.java | 137 +++++++++++++ src/alias/{ => id}/priority/PriorityNode.java | 4 +- src/alias/id/siteID/SiteIDEditor.java | 119 +++++------ src/alias/id/siteID/SiteIDNode.java | 2 +- .../id/{fleetsync => status}/StatusID.java | 2 +- src/alias/id/status/StatusIDEditor.java | 128 ++++++++++++ .../{fleetsync => status}/StatusIDNode.java | 4 +- src/alias/id/talkgroup/TalkgroupIDEditor.java | 120 +++++------ src/alias/id/talkgroup/TalkgroupIDNode.java | 2 +- src/alias/id/uniqueID/UniqueIDEditor.java | 130 ++++++------ src/alias/id/uniqueID/UniqueIDNode.java | 2 +- src/alias/priority/PriorityEditor.java | 191 ------------------ src/alias/record/NonRecordableEditor.java | 75 ------- src/audio/metadata/AudioMetadata.java | 2 +- .../decode/state/TrafficChannelManager.java | 2 +- 44 files changed, 1331 insertions(+), 945 deletions(-) create mode 100644 src/alias/ComponentEditor.java create mode 100644 src/alias/ComponentValidationException.java create mode 100644 src/alias/EmptyAliasActionEditor.java create mode 100644 src/alias/EmptyAliasIDEditor.java delete mode 100644 src/alias/id/fleetsync/StatusIDEditor.java rename src/alias/{record => id/nonrecordable}/NonRecordable.java (70%) create mode 100644 src/alias/id/nonrecordable/NonRecordableEditor.java rename src/alias/{record => id/nonrecordable}/NonRecordableNode.java (97%) rename src/alias/{ => id}/priority/Priority.java (98%) create mode 100644 src/alias/id/priority/PriorityEditor.java rename src/alias/{ => id}/priority/PriorityNode.java (97%) rename src/alias/id/{fleetsync => status}/StatusID.java (98%) create mode 100644 src/alias/id/status/StatusIDEditor.java rename src/alias/id/{fleetsync => status}/StatusIDNode.java (97%) delete mode 100644 src/alias/priority/PriorityEditor.java delete mode 100644 src/alias/record/NonRecordableEditor.java diff --git a/src/alias/Alias.java b/src/alias/Alias.java index d455b2de..1f29d060 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -27,7 +27,7 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import alias.action.AliasAction; -import alias.priority.Priority; +import alias.id.priority.Priority; @XmlSeeAlso( { AliasID.class, AliasAction.class } ) @XmlRootElement( name = "alias" ) diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 28e6ecb6..95d43e44 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -91,7 +91,7 @@ public class AliasEditor extends JPanel implements ActionListener, Listener getEditor( AliasID aliasID ) + { + if( aliasID != null ) + { + switch( aliasID.getType() ) + { + case ESN: + return new ESNEditor( aliasID ); + case Fleetsync: + return new FleetsyncIDEditor( aliasID ); + case LTRNetUID: + return new UniqueIDEditor( aliasID ); + case LoJack: + return new LoJackIDEditor( aliasID ); + case MDC1200: + return new MDC1200IDEditor( aliasID ); + case MIN: + return new MINEditor( aliasID ); + case MPT1327: + return new MPT1327IDEditor( aliasID ); + case NonRecordable: + return new NonRecordableEditor( aliasID ); + case Priority: + return new PriorityEditor( aliasID ); + case Site: + return new SiteIDEditor( aliasID ); + case Status: + return new StatusIDEditor( aliasID ); + case Talkgroup: + return new TalkgroupIDEditor( aliasID ); + default: + break; + } + } + + return new EmptyAliasIDEditor(); + } + + public static ComponentEditor getEditor( AliasAction aliasAction ) + { + if( aliasAction != null ) + { + if( aliasAction instanceof BeepAction ) + { + + } + else if( aliasAction instanceof ClipAction ) + { + + } + else if( aliasAction instanceof ScriptAction ) + { + + } + } + + return new EmptyAliasActionEditor(); + } } diff --git a/src/alias/AliasID.java b/src/alias/AliasID.java index 83a6c360..a4a8ec7f 100644 --- a/src/alias/AliasID.java +++ b/src/alias/AliasID.java @@ -22,16 +22,16 @@ import javax.xml.bind.annotation.XmlSeeAlso; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; -import alias.id.fleetsync.StatusID; import alias.id.lojack.LoJackFunctionAndID; import alias.id.mdc.MDC1200ID; import alias.id.mobileID.Min; import alias.id.mpt1327.MPT1327ID; +import alias.id.nonrecordable.NonRecordable; +import alias.id.priority.Priority; import alias.id.siteID.SiteID; +import alias.id.status.StatusID; import alias.id.talkgroup.TalkgroupID; import alias.id.uniqueID.UniqueID; -import alias.priority.Priority; -import alias.record.NonRecordable; @XmlSeeAlso( { Esn.class, FleetsyncID.class, Group.class, LoJackFunctionAndID.class, MDC1200ID.class, Min.class, MPT1327ID.class, NonRecordable.class, diff --git a/src/alias/AliasIdentifierEditor.java b/src/alias/AliasIdentifierEditor.java index a240e364..297ff4f3 100644 --- a/src/alias/AliasIdentifierEditor.java +++ b/src/alias/AliasIdentifierEditor.java @@ -7,18 +7,28 @@ import java.util.List; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JList; +import javax.swing.JOptionPane; +import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListModel; import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import net.miginfocom.swing.MigLayout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class AliasIdentifierEditor extends AliasConfigurationEditor { private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( AliasIdentifierEditor.class ); + private static ListModel EMPTY_MODEL = new DefaultListModel<>(); private JList mAliasIDList = new JList<>( EMPTY_MODEL ); + private EditorContainer mEditorContainer = new EditorContainer(); private Alias mAlias; @@ -30,14 +40,36 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor private void init() { setLayout( new MigLayout( "fill,wrap 3", - "[grow,fill][grow,fill][grow,fill]", "[grow,fill][][grow,fill]" ) ); + "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); - mAliasIDList.setVisibleRowCount( 6 ); + mAliasIDList.setVisibleRowCount( 4 ); mAliasIDList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); mAliasIDList.setLayoutOrientation( JList.VERTICAL ); + mAliasIDList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + JList list = (JList)event.getSource(); + + Object selectedItem = list.getSelectedValue(); + + if( selectedItem != null && selectedItem instanceof AliasID ) + { + AliasID selected = (AliasID)selectedItem; + + mEditorContainer.setAliasID( selected ); + } + } + } + } ); JScrollPane scroller = new JScrollPane( mAliasIDList ); add( scroller, "span,grow" ); + + add( mEditorContainer, "span,grow" ); add( new JButton( "New" ) ); add( new JButton( "Copy" ) ); @@ -75,6 +107,8 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor mAliasIDList.setModel( model ); } + + mEditorContainer.setAliasID( null ); } @Override @@ -83,4 +117,46 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor // TODO Auto-generated method stub } + + public class EditorContainer extends JPanel + { + private static final long serialVersionUID = 1L; + + private ComponentEditor mEditor = new EmptyAliasIDEditor(); + + public EditorContainer() + { + setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) ); + + add( mEditor ); + } + + public void setAliasID( AliasID aliasID ) + { + if( mEditor != null ) + { + if( mEditor.isModified() ) + { + int option = JOptionPane.showConfirmDialog( + EditorContainer.this, + "Identifier settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + mEditor.save(); + } + } + } + removeAll(); + + mEditor = AliasFactory.getEditor( aliasID ); + + add( mEditor ); + + revalidate(); + } + } } diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index 312e9d6f..52eb6efe 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -36,16 +36,16 @@ import alias.action.clip.ClipAction; import alias.action.script.ScriptAction; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; -import alias.id.fleetsync.StatusID; import alias.id.lojack.LoJackFunctionAndID; import alias.id.mdc.MDC1200ID; import alias.id.mobileID.Min; import alias.id.mpt1327.MPT1327ID; +import alias.id.nonrecordable.NonRecordable; +import alias.id.priority.Priority; import alias.id.siteID.SiteID; +import alias.id.status.StatusID; import alias.id.talkgroup.TalkgroupID; import alias.id.uniqueID.UniqueID; -import alias.priority.Priority; -import alias.record.NonRecordable; import audio.metadata.MetadataType; @XmlSeeAlso( { Alias.class, diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index 69768e3a..8455f836 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -50,6 +50,10 @@ public class AliasNameEditor extends AliasConfigurationEditor setLayout( new MigLayout( "fill,wrap 2", "[right][grow,fill]", "[][][][][][][grow]" ) ); + add( new JLabel( "Name:" ) ); + mName = new JTextField(); + add( mName, "wrap" ); + add( new JLabel( "List:" ) ); mListCombo.setEditable( true ); mListCombo.addActionListener( new ActionListener() @@ -80,10 +84,6 @@ public class AliasNameEditor extends AliasConfigurationEditor mGroupCombo.setEditable( true ); add( mGroupCombo, "wrap" ); - add( new JLabel( "Name:" ) ); - mName = new JTextField(); - add( mName, "wrap" ); - add( new JLabel( "Color:" ) ); mButtonColor = new JButton( "Select ..." ); diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java index 7346bb4c..ca120982 100644 --- a/src/alias/AliasNode.java +++ b/src/alias/AliasNode.java @@ -40,8 +40,6 @@ import alias.id.esn.ESNNode; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; import alias.id.fleetsync.FleetsyncIDNode; -import alias.id.fleetsync.StatusID; -import alias.id.fleetsync.StatusIDNode; import alias.id.lojack.LoJackFunctionAndID; import alias.id.lojack.LoJackIDNode; import alias.id.mdc.MDC1200ID; @@ -50,16 +48,18 @@ import alias.id.mobileID.MINNode; import alias.id.mobileID.Min; import alias.id.mpt1327.MPT1327ID; import alias.id.mpt1327.MPT1327IDNode; +import alias.id.nonrecordable.NonRecordable; +import alias.id.nonrecordable.NonRecordableNode; +import alias.id.priority.Priority; +import alias.id.priority.PriorityNode; import alias.id.siteID.SiteID; import alias.id.siteID.SiteIDNode; +import alias.id.status.StatusID; +import alias.id.status.StatusIDNode; import alias.id.talkgroup.TalkgroupID; import alias.id.talkgroup.TalkgroupIDNode; import alias.id.uniqueID.UniqueID; import alias.id.uniqueID.UniqueIDNode; -import alias.priority.Priority; -import alias.priority.PriorityNode; -import alias.record.NonRecordable; -import alias.record.NonRecordableNode; import controller.ConfigurableNode; public class AliasNode extends ConfigurableNode diff --git a/src/alias/ComponentEditor.java b/src/alias/ComponentEditor.java new file mode 100644 index 00000000..3de3d30c --- /dev/null +++ b/src/alias/ComponentEditor.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias; + +import javax.swing.JPanel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public abstract class ComponentEditor extends JPanel implements DocumentListener +{ + private static final long serialVersionUID = 1L; + private boolean mModified; + private ComponentModificationListener mListener; + protected T mComponent; + + /** + * Abstract class for editor with notifications when contents are modified + * and methods for saving or resetting the component values with the + * contents of the editor. + */ + public ComponentEditor( T component ) + { + mComponent = component; + } + + public ComponentEditor() + { + } + + public void dispose() + { + mComponent = null; + mListener = null; + } + + public T getComponent() + { + return mComponent; + } + + protected void setModified( boolean modified ) + { + mModified = modified; + + if( modified && mListener != null ) + { + mListener.componentModified(); + } + } + + /** + * Indicates if the contents of the editor have been modified + */ + public boolean isModified() + { + return mModified; + } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ) { } + + public void setComponentModificationListener( ComponentModificationListener listener ) + { + mListener = listener; + } + + /** + * Sets or resets the editable component + */ + public abstract void setComponent( T t ); + + /** + * Saves changed editor values to the component + */ + public abstract void save(); + + /** + * Reloads the editor contents from the source component and resets the + * modified flag + */ + public void reset() + { + setComponent( getComponent() ); + } + + /** + * Validate contents of other editors against this component editor before + * saving. Override this method to allow controller to pass other editors + * to this editor for content inspection/validation before indicating that + * this editor passes validation. This allows for validating editor contents + * that are contingent on settings in other editors. + * + * @throws ComponentValidationException if the contents of the editor + * argument cannot be validated, with the exception containing a detailed + * description of the issue. + */ + public void validate( ComponentEditor editor ) throws ComponentValidationException + { + } + + /** + * Listener interface to receive notification that the contents of the + * editor have been modified from the original component value and either + * need to be saved or reset + */ + public interface ComponentModificationListener + { + public void componentModified(); + } +} diff --git a/src/alias/ComponentValidationException.java b/src/alias/ComponentValidationException.java new file mode 100644 index 00000000..bfa8d925 --- /dev/null +++ b/src/alias/ComponentValidationException.java @@ -0,0 +1,14 @@ +package alias; + +public class ComponentValidationException extends Exception +{ + private static final long serialVersionUID = 1L; + + /** + * Component editor contents validation error + */ + public ComponentValidationException( String message ) + { + super( message ); + } +} diff --git a/src/alias/EmptyAliasActionEditor.java b/src/alias/EmptyAliasActionEditor.java new file mode 100644 index 00000000..d00dfaa7 --- /dev/null +++ b/src/alias/EmptyAliasActionEditor.java @@ -0,0 +1,32 @@ +package alias; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; +import alias.action.AliasAction; + +public class EmptyAliasActionEditor extends ComponentEditor +{ + private static final long serialVersionUID = 1L; + + public EmptyAliasActionEditor() + { + setLayout( new MigLayout( "", "[center]", "[][grow]" ) ); + add( new JLabel( "Please select an alias action" ), "wrap" ); + } + + @Override + public void setComponent( AliasAction t ) + { + } + + @Override + public void save() + { + } + + @Override + public void reset() + { + } +} diff --git a/src/alias/EmptyAliasIDEditor.java b/src/alias/EmptyAliasIDEditor.java new file mode 100644 index 00000000..3c7908f6 --- /dev/null +++ b/src/alias/EmptyAliasIDEditor.java @@ -0,0 +1,31 @@ +package alias; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; + +public class EmptyAliasIDEditor extends ComponentEditor +{ + private static final long serialVersionUID = 1L; + + public EmptyAliasIDEditor() + { + setLayout( new MigLayout( "", "[center]", "[][grow]" ) ); + add( new JLabel( "Please select an identifier" ), "wrap" ); + } + + @Override + public void setComponent( AliasID t ) + { + } + + @Override + public void save() + { + } + + @Override + public void reset() + { + } +} diff --git a/src/alias/id/esn/ESNEditor.java b/src/alias/id/esn/ESNEditor.java index e2a55f2b..1ace4da8 100644 --- a/src/alias/id/esn/ESNEditor.java +++ b/src/alias/id/esn/ESNEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,76 +20,86 @@ package alias.id.esn; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class ESNEditor extends JPanel implements ActionListener +public class ESNEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private ESNNode mESNNode; - private JTextField mTextESN; - public ESNEditor( ESNNode esnNode ) + private static final String HELP_TEXT = "An Electronic Serial Number (ESN)" + + " is a unique radio hardware identifier normally composed of" + + " hexadecimal (0-9,A-F) characters. Use an asterisk (*) to wildcard" + + " individual digits (e.g. ABCD123* or AB**1**4)"; + + private JTextField mTextField; + + public ESNEditor( AliasID aliasID ) { - mESNNode = esnNode; + super( aliasID ); initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "Electronic Serial Number ID" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); add( new JLabel( "ESN:" ) ); - mTextESN = new JTextField( mESNNode.getESN().getEsn() ); - add( mTextESN, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ESNEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ESNEditor.this ); - add( btnReset, "growx,push" ); + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public Esn getEsn() + { + if( getComponent() instanceof Esn ) + { + return (Esn)getComponent(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + Esn esn = getEsn(); + + if( esn != null ) { - String esn = mTextESN.getText(); - - if( esn != null ) - { - mESNNode.getESN().setEsn( esn ); - - ((ConfigurableNode)mESNNode.getParent()).sort(); - - mESNNode.save(); - - mESNNode.show(); - } - else - { - JOptionPane.showMessageDialog( ESNEditor.this, "Please enter an ESN" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextESN.setText( mESNNode.getESN().getEsn() ); + mTextField.setText( esn.getEsn() ); } - mESNNode.refresh(); - } + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + Esn esn = getEsn(); + + if( esn != null ) + { + esn.setEsn( mTextField.getText() ); + } + + setModified( false ); + } } diff --git a/src/alias/id/esn/ESNNode.java b/src/alias/id/esn/ESNNode.java index ecdd33e8..a814811e 100644 --- a/src/alias/id/esn/ESNNode.java +++ b/src/alias/id/esn/ESNNode.java @@ -41,7 +41,7 @@ public class ESNNode extends AliasIDNode @Override public JPanel getEditor() { - return new ESNEditor( this ); + return null; } public Esn getESN() diff --git a/src/alias/id/fleetsync/FleetsyncIDEditor.java b/src/alias/id/fleetsync/FleetsyncIDEditor.java index 03b36c81..9075ee7f 100644 --- a/src/alias/id/fleetsync/FleetsyncIDEditor.java +++ b/src/alias/id/fleetsync/FleetsyncIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,89 +20,107 @@ package alias.id.fleetsync; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.JButton; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import controller.channel.Channel; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class FleetsyncIDEditor extends JPanel implements ActionListener +public class FleetsyncIDEditor extends ComponentEditor { - private static final long serialVersionUID = 1L; - private FleetsyncIDNode mFleetsyncIDNode; - - private JLabel mLabelName; - private JTextField mTextIdent; + private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class ); - private String mHelpText = "Enter a formatted fleetsync identifier.\n\n" - + "Format: PPP-IIII (P=Prefix, I=Identifier)\n\n" - + "Wildcard: use one or more asterisks (*) for any talkgroup " - + "digits.\n\n"; + private static final long serialVersionUID = 1L; - public FleetsyncIDEditor( FleetsyncIDNode fsNode ) + private static final String HELP_TEXT = "A Fleetsync identifier is a" + + " composite decimal [0-9] value formatted as ggg-uuuu where" + + " g=Group and u=Unit."; + + private JTextField mTextField; + + public FleetsyncIDEditor( AliasID aliasID ) { - mFleetsyncIDNode = fsNode; + super( aliasID ); + initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); - add( new JLabel( "Fleetsync ID" ), "span,align center" ); + add( new JLabel( "Fleetsync ID:" ) ); - add( new JLabel( "Ident:" ) ); - mTextIdent = new JTextField( mFleetsyncIDNode.getFleetsyncID().getIdent() ); + MaskFormatter formatter = null; - add( mTextIdent, "grow, wrap" ); + try + { + //Mask: 3 digits - 4 digits + formatter = new MaskFormatter( "###-####" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( FleetsyncIDEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( FleetsyncIDEditor.this ); - add( btnReset, "growx,push" ); + add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( mHelpText ); + JTextArea helpText = new JTextArea( HELP_TEXT ); helpText.setLineWrap( true ); helpText.setBackground( getBackground() ); add( helpText, "span,grow,push" ); } + + public FleetsyncID getFleetsyncID() + { + if( getComponent() instanceof FleetsyncID ) + { + return (FleetsyncID)getComponent(); + } + + return null; + } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + FleetsyncID fleetsync = getFleetsyncID(); + + if( fleetsync != null ) { - String ident = mTextIdent.getText(); - - if( ident != null ) - { - mFleetsyncIDNode.getFleetsyncID().setIdent( ident ); + mTextField.setText( fleetsync.getIdent() ); + } + + setModified( false ); + + repaint(); + } - ((ConfigurableNode)mFleetsyncIDNode.getParent()).sort(); - - mFleetsyncIDNode.save(); - - mFleetsyncIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( FleetsyncIDEditor.this, "Please enter an ident" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + FleetsyncID fleetsync = getFleetsyncID(); + + if( fleetsync != null ) { - mTextIdent.setText( mFleetsyncIDNode.getFleetsyncID().getIdent() ); + fleetsync.setIdent( mTextField.getText() ); } - mFleetsyncIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/fleetsync/FleetsyncIDNode.java b/src/alias/id/fleetsync/FleetsyncIDNode.java index 4d0e09f1..280c44a8 100644 --- a/src/alias/id/fleetsync/FleetsyncIDNode.java +++ b/src/alias/id/fleetsync/FleetsyncIDNode.java @@ -41,7 +41,7 @@ public class FleetsyncIDNode extends AliasIDNode @Override public JPanel getEditor() { - return new FleetsyncIDEditor( this ); + return null; } public FleetsyncID getFleetsyncID() diff --git a/src/alias/id/fleetsync/StatusIDEditor.java b/src/alias/id/fleetsync/StatusIDEditor.java deleted file mode 100644 index fe3a6348..00000000 --- a/src/alias/id/fleetsync/StatusIDEditor.java +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.fleetsync; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.ConfigurableNode; - -public class StatusIDEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( StatusIDEditor.class ); - - private StatusIDNode mStatusIDNode; - - private JLabel mLabelName; - private JTextField mTextStatusID; - - public StatusIDEditor( StatusIDNode statusIDNode ) - { - mStatusIDNode = statusIDNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout() ); - - setBorder( BorderFactory.createTitledBorder( "Status Code" ) ); - - mLabelName = new JLabel( "Status Code:" ); - add( mLabelName, "align right" ); - - mTextStatusID = new JTextField( - String.valueOf( mStatusIDNode.getStatusID().getStatus() ) ); - add( mTextStatusID, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( StatusIDEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( StatusIDEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - String statusIDString = mTextStatusID.getText(); - - if( statusIDString != null ) - { - try - { - int id = Integer.parseInt( statusIDString ); - - mStatusIDNode.getStatusID().setStatus( id ); - - ((ConfigurableNode)mStatusIDNode.getParent()).sort(); - - mStatusIDNode.save(); - - mStatusIDNode.show(); - } - catch( Exception ex ) - { - mLog.error( "StatusIDEditor - error parsing int status " + - "id from [" + statusIDString + "]" ); - } - } - else - { - JOptionPane.showMessageDialog( StatusIDEditor.this, - "Please enter a status ID code" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextStatusID.setText( - String.valueOf( mStatusIDNode.getStatusID().getStatus() ) ); - } - - mStatusIDNode.refresh(); - } -} diff --git a/src/alias/id/lojack/LoJackIDEditor.java b/src/alias/id/lojack/LoJackIDEditor.java index 9cc121da..551dc82d 100644 --- a/src/alias/id/lojack/LoJackIDEditor.java +++ b/src/alias/id/lojack/LoJackIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,109 +20,125 @@ package alias.id.lojack; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.JButton; import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import module.decode.lj1200.LJ1200Message; import module.decode.lj1200.LJ1200Message.Function; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class LoJackIDEditor extends JPanel implements ActionListener +public class LoJackIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private LoJackIDNode mLoJackIDNode; - + + private JTextField mTextField; + private JComboBox mFunctionCombo; - private JTextField mTextIdent; - private String mHelpText = "Select a LoJack message function code and enter" - + " a five character reply code/identifier in the ID field.\n\n" - + "Wildcard: use an asterisk (*) to wildcard any character in the " - + "five character reply code (e.g. AB*CD or ***12 or *****)\n\n" - + "Reply codes are five characters where the middle character" - + " identifies the entity (tower/site or transponder) as follows:\n" - + "X,Y\t\tTower\n" - + "0-9,A,C-H,J-N,P-W\tTransponder\n" - + "B,I,O,Z\t\tNot Used"; + private static final String HELP_TEXT = "LoJack function code and five character ID." + + " The middle character in a reply ID code identifies the entity:" + + " Tower[X,Y] Transponder[0-9,A,C-H,J-N,P-W] Not Used[B,I,O,Z]." + + " Use an asterisk (*) to wildcard ID characters (e.g. AB*CD or ***12 or *****)"; - public LoJackIDEditor( LoJackIDNode lojackNode ) + public LoJackIDEditor( AliasID aliasID ) { - mLoJackIDNode = lojackNode; + super( aliasID ); + initGUI(); + + setComponent( aliasID ); } private void initGUI() { setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - add( new JLabel( "LoJack Function and ID" ), "span,align center" ); - - LoJackFunctionAndID lojack = mLoJackIDNode.getLoJackID(); - add( new JLabel( "Function:" ) ); mFunctionCombo = new JComboBox( Function.values() ); - mFunctionCombo.setSelectedItem( lojack.getFunction() ); - add( mFunctionCombo, "grow, wrap" ); + mFunctionCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mFunctionCombo, "growx, push" ); add( new JLabel( "ID:" ) ); - mTextIdent = new JTextField( lojack.getID() ); - - add( mTextIdent, "grow, wrap" ); - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( LoJackIDEditor.this ); - add( btnSave, "growx,push" ); + MaskFormatter formatter = null; - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( LoJackIDEditor.this ); - add( btnReset, "growx,push" ); + try + { + //Mask: any character or number, 5 places + formatter = new MaskFormatter( "AAAAA" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - JTextArea helpText = new JTextArea( mHelpText ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); helpText.setLineWrap( true ); helpText.setBackground( getBackground() ); add( helpText, "span,grow,push" ); } + + public LoJackFunctionAndID getLoJackID() + { + if( getComponent() instanceof LoJackFunctionAndID ) + { + return (LoJackFunctionAndID)getComponent(); + } + + return null; + } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - final LoJackFunctionAndID lojack = mLoJackIDNode.getLoJackID(); - - if( command.contentEquals( "Save" ) ) - { - String id = mTextIdent.getText(); - - if( id != null ) - { - - lojack.setID( id ); - lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() ); - - ((ConfigurableNode)mLoJackIDNode.getParent()).sort(); - - mLoJackIDNode.save(); - - mLoJackIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( LoJackIDEditor.this, "Please enter an ident" ); - } - } - else if( command.contentEquals( "Reset" ) ) + LoJackFunctionAndID lojack = getLoJackID(); + + if( lojack != null ) { mFunctionCombo.setSelectedItem( lojack.getFunction() ); - mTextIdent.setText( lojack.getID() ); + mTextField.setText( lojack.getID() ); + } + else + { + mFunctionCombo.setSelectedItem( null ); + mTextField.setText( null ); } - mLoJackIDNode.refresh(); - } + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + LoJackFunctionAndID lojack = getLoJackID(); + + if( lojack != null ) + { + lojack.setID( mTextField.getText() ); + lojack.setFunction( (Function)mFunctionCombo.getSelectedItem() ); + } + + setModified( false ); + } } diff --git a/src/alias/id/lojack/LoJackIDNode.java b/src/alias/id/lojack/LoJackIDNode.java index fcda77bd..b5e65b7f 100644 --- a/src/alias/id/lojack/LoJackIDNode.java +++ b/src/alias/id/lojack/LoJackIDNode.java @@ -41,7 +41,7 @@ public class LoJackIDNode extends AliasIDNode @Override public JPanel getEditor() { - return new LoJackIDEditor( this ); + return null; } public LoJackFunctionAndID getLoJackID() diff --git a/src/alias/id/mdc/MDC1200IDEditor.java b/src/alias/id/mdc/MDC1200IDEditor.java index 6003fd56..2f23bacb 100644 --- a/src/alias/id/mdc/MDC1200IDEditor.java +++ b/src/alias/id/mdc/MDC1200IDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,79 +17,100 @@ ******************************************************************************/ package alias.id.mdc; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class MDC1200IDEditor extends JPanel implements ActionListener +public class MDC1200IDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private MDC1200IDNode mMDC1200IDNode; - - private JTextField mTextIdent; - public MDC1200IDEditor( MDC1200IDNode fsNode ) + private static final String HELP_TEXT = "An MDC-1200 identifier is a" + + " four digit [0-9] value. Use an asterisk (*) to wildcard" + + " individual digits (e.g. 123* or **34)"; + + private JTextField mTextField; + + public MDC1200IDEditor( AliasID aliasID ) { - mMDC1200IDNode = fsNode; + super( aliasID ); + initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); - add( new JLabel( "MDC-1200 " ), "span,align center" ); + add( new JLabel( "MDC-1200 ID:" ) ); - add( new JLabel( "Unit ID:" ) ); - mTextIdent = new JTextField( mMDC1200IDNode.getMDC1200ID().getIdent() ); - add( mTextIdent, "growx,push" ); + MaskFormatter formatter = null; + + try + { + //Mask: 4 digits + formatter = new MaskFormatter( "####" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( MDC1200IDEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( MDC1200IDEditor.this ); - add( btnReset, "growx,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public MDC1200ID getMDC1200ID() + { + if( getComponent() instanceof MDC1200ID ) + { + return (MDC1200ID)getComponent(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + MDC1200ID mdc = getMDC1200ID(); + + if( mdc != null ) { - String ident = mTextIdent.getText(); - - if( ident != null ) - { - mMDC1200IDNode.getMDC1200ID().setIdent( ident ); + mTextField.setText( mdc.getIdent() ); + } + + setModified( false ); + + repaint(); + } - ((ConfigurableNode)mMDC1200IDNode.getParent()).sort(); - - mMDC1200IDNode.save(); - - mMDC1200IDNode.show(); - } - else - { - JOptionPane.showMessageDialog( MDC1200IDEditor.this, "Please enter a unit ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + MDC1200ID mdc = getMDC1200ID(); + + if( mdc != null ) { - mTextIdent.setText( mMDC1200IDNode.getMDC1200ID().getIdent() ); + mdc.setIdent( mTextField.getText() ); } - mMDC1200IDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/mdc/MDC1200IDNode.java b/src/alias/id/mdc/MDC1200IDNode.java index 9c315a49..0af5fae0 100644 --- a/src/alias/id/mdc/MDC1200IDNode.java +++ b/src/alias/id/mdc/MDC1200IDNode.java @@ -41,7 +41,7 @@ public class MDC1200IDNode extends AliasIDNode @Override public JPanel getEditor() { - return new MDC1200IDEditor( this ); + return null; } public MDC1200ID getMDC1200ID() diff --git a/src/alias/id/mobileID/MINEditor.java b/src/alias/id/mobileID/MINEditor.java index 8b0f982a..aaac927d 100644 --- a/src/alias/id/mobileID/MINEditor.java +++ b/src/alias/id/mobileID/MINEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,79 +20,99 @@ package alias.id.mobileID; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.JButton; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class MINEditor extends JPanel implements ActionListener +public class MINEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private MINNode mMINNode; - - private JLabel mLabelName; - private JTextField mTextMIN; - public MINEditor( MINNode minNode ) + private static final String HELP_TEXT = "A Passport Mobile Identifier (MIN) is a" + + " six character hexadecimal value"; + + private JTextField mTextField; + + public MINEditor( AliasID aliasID ) { - mMINNode = minNode; + super( aliasID ); initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "Mobile ID Number (MIN)" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); add( new JLabel( "MIN:" ) ); - - mTextMIN = new JTextField( mMINNode.getMIN().getMin() ); - add( mTextMIN, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( MINEditor.this ); - add( btnSave, "growx,push" ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( MINEditor.this ); - add( btnReset, "growx,push" ); + MaskFormatter formatter = null; + + try + { + //Mask: 6 hex characters + formatter = new MaskFormatter( "HHHHHH" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } + + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public Min getMin() + { + if( getComponent() instanceof Min ) + { + return (Min)getComponent(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + Min min = getMin(); + + if( min != null ) { - String esn = mTextMIN.getText(); - - if( esn != null ) - { - mMINNode.getMIN().setMin( esn ); - - ((ConfigurableNode)mMINNode.getParent()).sort(); - - mMINNode.save(); - - mMINNode.show(); - } - else - { - JOptionPane.showMessageDialog( MINEditor.this, "Please enter an ESN" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextMIN.setText( mMINNode.getMIN().getMin() ); + mTextField.setText( min.getMin() ); } - mMINNode.refresh(); - } + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + Min min = getMin(); + + if( min != null ) + { + min.setMin( mTextField.getText() ); + } + + setModified( false ); + } } diff --git a/src/alias/id/mobileID/MINNode.java b/src/alias/id/mobileID/MINNode.java index be0b7e83..3f43fca2 100644 --- a/src/alias/id/mobileID/MINNode.java +++ b/src/alias/id/mobileID/MINNode.java @@ -41,7 +41,7 @@ public class MINNode extends AliasIDNode @Override public JPanel getEditor() { - return new MINEditor( this ); + return null; } public Min getMIN() diff --git a/src/alias/id/mpt1327/MPT1327IDEditor.java b/src/alias/id/mpt1327/MPT1327IDEditor.java index ab6dcdb6..c1e7e674 100644 --- a/src/alias/id/mpt1327/MPT1327IDEditor.java +++ b/src/alias/id/mpt1327/MPT1327IDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,80 +17,101 @@ ******************************************************************************/ package alias.id.mpt1327; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; +import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class MPT1327IDEditor extends JPanel implements ActionListener +public class MPT1327IDEditor extends ComponentEditor { - private static final long serialVersionUID = 1L; - private MPT1327IDNode mMPT1327IDNode; - - private JTextField mTextIdent; + private static final long serialVersionUID = 1L; - public MPT1327IDEditor( MPT1327IDNode fsNode ) + private static final String HELP_TEXT = "An MPT-1327 identifier is a" + + " composite decimal [0-9] value formatted as ppp-iiii where" + + " p=Prefix and i=Ident."; + + private JTextField mTextField; + + public MPT1327IDEditor( AliasID aliasID ) { - mMPT1327IDNode = fsNode; + super( aliasID ); + initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); - add( new JLabel( "MPT-1327 Radio or Group ID" ), "span,align center" ); + add( new JLabel( "MPT-1327 ID:" ) ); - add( new JLabel( "ID:" ) ); + MaskFormatter formatter = null; + + try + { + //Mask: 3 digits - 4 digits + formatter = new MaskFormatter( "###-####" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } - mTextIdent = new JTextField( mMPT1327IDNode.getMPT1327ID().getIdent() ); - add( mTextIdent, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( MPT1327IDEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( MPT1327IDEditor.this ); - add( btnReset, "growx,push" ); + add( mTextField, "growx,push" ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public MPT1327ID getMPT1327ID() + { + if( getComponent() instanceof MPT1327ID ) + { + return (MPT1327ID)getComponent(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + MPT1327ID mpt = getMPT1327ID(); + + if( mpt != null ) { - String ident = mTextIdent.getText(); - - if( ident != null ) - { - mMPT1327IDNode.getMPT1327ID().setIdent( ident ); + mTextField.setText( mpt.getIdent() ); + } + + setModified( false ); + + repaint(); + } - ((ConfigurableNode)mMPT1327IDNode.getParent()).sort(); - - mMPT1327IDNode.save(); - - mMPT1327IDNode.show(); - } - else - { - JOptionPane.showMessageDialog( MPT1327IDEditor.this, "Please enter a MPT1327 unit ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + MPT1327ID mpt = getMPT1327ID(); + + if( mpt != null ) { - mTextIdent.setText( mMPT1327IDNode.getMPT1327ID().getIdent() ); + mpt.setIdent( mTextField.getText() ); } - mMPT1327IDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/mpt1327/MPT1327IDNode.java b/src/alias/id/mpt1327/MPT1327IDNode.java index a5937cee..04cda43a 100644 --- a/src/alias/id/mpt1327/MPT1327IDNode.java +++ b/src/alias/id/mpt1327/MPT1327IDNode.java @@ -41,7 +41,7 @@ public class MPT1327IDNode extends AliasIDNode @Override public JPanel getEditor() { - return new MPT1327IDEditor( this ); + return null; } public MPT1327ID getMPT1327ID() diff --git a/src/alias/record/NonRecordable.java b/src/alias/id/nonrecordable/NonRecordable.java similarity index 70% rename from src/alias/record/NonRecordable.java rename to src/alias/id/nonrecordable/NonRecordable.java index 00c69482..423ccd56 100644 --- a/src/alias/record/NonRecordable.java +++ b/src/alias/id/nonrecordable/NonRecordable.java @@ -1,4 +1,4 @@ -package alias.record; +package alias.id.nonrecordable; import alias.AliasID; import alias.AliasIDType; @@ -16,4 +16,10 @@ public class NonRecordable extends AliasID { return false; } + + @Override + public String toString() + { + return "Non-Recordable"; + } } diff --git a/src/alias/id/nonrecordable/NonRecordableEditor.java b/src/alias/id/nonrecordable/NonRecordableEditor.java new file mode 100644 index 00000000..220a9d5d --- /dev/null +++ b/src/alias/id/nonrecordable/NonRecordableEditor.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias.id.nonrecordable; + +import javax.swing.JTextArea; + +import net.miginfocom.swing.MigLayout; +import alias.AliasID; +import alias.ComponentEditor; + +public class NonRecordableEditor extends ComponentEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "This identifies an alias as " + + "non-recordable. The alias can still be monitored but it will " + + "not be recorded when recording is enabled for the decoding channel."; + + public NonRecordableEditor( AliasID aliasID ) + { + super( aliasID ); + + initGUI(); + + setComponent( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][grow]" ) ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + @Override + public void setComponent( AliasID aliasID ) + { + } + + @Override + public void save() + { + } +} diff --git a/src/alias/record/NonRecordableNode.java b/src/alias/id/nonrecordable/NonRecordableNode.java similarity index 97% rename from src/alias/record/NonRecordableNode.java rename to src/alias/id/nonrecordable/NonRecordableNode.java index 082238df..2a9a2fa4 100644 --- a/src/alias/record/NonRecordableNode.java +++ b/src/alias/id/nonrecordable/NonRecordableNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.record; +package alias.id.nonrecordable; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -42,7 +42,7 @@ public class NonRecordableNode extends AliasIDNode @Override public JPanel getEditor() { - return new NonRecordableEditor( this ); + return null; } @Override diff --git a/src/alias/priority/Priority.java b/src/alias/id/priority/Priority.java similarity index 98% rename from src/alias/priority/Priority.java rename to src/alias/id/priority/Priority.java index 6a9aa177..d15a75f0 100644 --- a/src/alias/priority/Priority.java +++ b/src/alias/id/priority/Priority.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.priority; +package alias.id.priority; import javax.xml.bind.annotation.XmlAttribute; diff --git a/src/alias/id/priority/PriorityEditor.java b/src/alias/id/priority/PriorityEditor.java new file mode 100644 index 00000000..ccdbb36a --- /dev/null +++ b/src/alias/id/priority/PriorityEditor.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias.id.priority; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.JTextArea; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import net.miginfocom.swing.MigLayout; +import alias.AliasID; +import alias.ComponentEditor; + +public class PriorityEditor extends ComponentEditor +{ + private static final long serialVersionUID = 1L; + + public static final String DO_NOT_MONITOR = "Do Not Monitor"; + + private static final String HELP_TEXT = "Priority determines which calls" + + " have priority for playback over your computer speakers, or designates" + + " an alias for non-monitoring if you don't want to hear calls" + + " from an alias. Lower values indicate higher priority levels."; + + private JCheckBox mDoNotMonitorCheckBox = new JCheckBox( DO_NOT_MONITOR ); + private JSlider mPrioritySlider; + private JLabel mPrioritySliderLabel; + + public PriorityEditor( AliasID aliasID ) + { + super( aliasID ); + + initGUI(); + + setComponent( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + + add( mDoNotMonitorCheckBox, "span,align center" ); + + mPrioritySlider = new JSlider( JSlider.HORIZONTAL, + Priority.MIN_PRIORITY, + Priority.MAX_PRIORITY, + Priority.MIN_PRIORITY ); + + mPrioritySlider.setMajorTickSpacing( 20 ); + mPrioritySlider.setMinorTickSpacing( 5 ); + mPrioritySlider.setPaintTicks( true ); + mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); + mPrioritySlider.setPaintLabels( true ); + mPrioritySlider.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); + } + } ); + + mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); + add( mPrioritySliderLabel ); + add( mPrioritySlider, "wrap,grow" ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public Priority getPriority() + { + if( getComponent() instanceof Priority ) + { + return (Priority)getComponent(); + } + + return null; + } + + @Override + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; + + Priority priority = getPriority(); + + if( priority != null ) + { + mPrioritySlider.setValue( priority.getPriority() ); + mDoNotMonitorCheckBox.setSelected( priority.isDoNotMonitor() ); + } + + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + Priority priority = getPriority(); + + if( priority != null ) + { + if( mDoNotMonitorCheckBox.isSelected() ) + { + priority.setPriority( Priority.DO_NOT_MONITOR ); + } + else + { + priority.setPriority( mPrioritySlider.getValue() ); + } + } + + setModified( false ); + } +} diff --git a/src/alias/priority/PriorityNode.java b/src/alias/id/priority/PriorityNode.java similarity index 97% rename from src/alias/priority/PriorityNode.java rename to src/alias/id/priority/PriorityNode.java index 707ae0f7..2a448d48 100644 --- a/src/alias/priority/PriorityNode.java +++ b/src/alias/id/priority/PriorityNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.priority; +package alias.id.priority; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -41,7 +41,7 @@ public class PriorityNode extends AliasIDNode @Override public JPanel getEditor() { - return new PriorityEditor( this ); + return null; } public Priority getPriority() diff --git a/src/alias/id/siteID/SiteIDEditor.java b/src/alias/id/siteID/SiteIDEditor.java index 526e89c2..a352fd94 100644 --- a/src/alias/id/siteID/SiteIDEditor.java +++ b/src/alias/id/siteID/SiteIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,95 +17,86 @@ ******************************************************************************/ package alias.id.siteID; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; +import alias.AliasID; +import alias.ComponentEditor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class SiteIDEditor extends JPanel implements ActionListener +public class SiteIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( SiteIDEditor.class ); - private SiteIDNode mSiteIDNode; - private JTextField mTextSiteID; - public SiteIDEditor( SiteIDNode siteIDNode ) + private static final String HELP_TEXT = "Site number for P25 systems uses" + + " the format RR-SS, where RR = RF Subsystem and SS = Site Number. " + + " (e.g. RFSS 1 Site 3: 01-03). For other protocols, simply use" + + " a decimal value for the site ID"; + + private JTextField mTextField; + + public SiteIDEditor( AliasID aliasID ) { - mSiteIDNode = siteIDNode; + super( aliasID ); initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); - add( new JLabel( "Site ID" ), "span,align center" ); + add( new JLabel( "Site ID:" ) ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); - add( new JLabel( "Site:" ) ); + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public SiteID getSiteID() + { + if( getComponent() instanceof SiteID ) + { + return (SiteID)getComponent(); + } - mTextSiteID = new JTextField( - String.valueOf( mSiteIDNode.getSiteID().getSite() ) ); - add( mTextSiteID, "growx,push" ); - - JTextArea description = new JTextArea( "Enter a site number. " - + "For P25 systems, use the format RR-SS, where RR = RF " - + "Subsystem and SS = Site Number. For example, RFSS 1 and " - + "Site 3 would be: 01-03"); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( SiteIDEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( SiteIDEditor.this ); - add( btnReset, "growx,push" ); + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + SiteID siteID = getSiteID(); + + if( siteID != null ) { - String siteIDString = mTextSiteID.getText(); - - if( siteIDString != null ) - { - mSiteIDNode.getSiteID().setSite( siteIDString ); + mTextField.setText( siteID.getSite() ); + } + + setModified( false ); + + repaint(); + } - mSiteIDNode.save(); - - mSiteIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( SiteIDEditor.this, - "Please enter a site ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + SiteID siteID = getSiteID(); + + if( siteID != null ) { - mTextSiteID.setText( mSiteIDNode.getSiteID().getSite() ); + siteID.setSite( mTextField.getText() ); } - mSiteIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/siteID/SiteIDNode.java b/src/alias/id/siteID/SiteIDNode.java index e9e2a4b4..52cc308b 100644 --- a/src/alias/id/siteID/SiteIDNode.java +++ b/src/alias/id/siteID/SiteIDNode.java @@ -41,7 +41,7 @@ public class SiteIDNode extends AliasIDNode @Override public JPanel getEditor() { - return new SiteIDEditor( this ); + return null; } public SiteID getSiteID() diff --git a/src/alias/id/fleetsync/StatusID.java b/src/alias/id/status/StatusID.java similarity index 98% rename from src/alias/id/fleetsync/StatusID.java rename to src/alias/id/status/StatusID.java index bbcd694c..40ae82b8 100644 --- a/src/alias/id/fleetsync/StatusID.java +++ b/src/alias/id/status/StatusID.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.id.fleetsync; +package alias.id.status; import javax.xml.bind.annotation.XmlAttribute; diff --git a/src/alias/id/status/StatusIDEditor.java b/src/alias/id/status/StatusIDEditor.java new file mode 100644 index 00000000..d0575e2c --- /dev/null +++ b/src/alias/id/status/StatusIDEditor.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias.id.status; + +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.text.MaskFormatter; + +import net.miginfocom.swing.MigLayout; +import alias.AliasID; +import alias.ComponentEditor; + +public class StatusIDEditor extends ComponentEditor +{ + private static final long serialVersionUID = 1L; + + private static final String HELP_TEXT = "Status numbers are used in some" + + " protocols like Fleetsync. The status number is assigned a meaning" + + " in the radio. You can assign a 3 digit status code (use leading zeros) to an" + + " alias where the alias contains the status meaning."; + + private JTextField mTextField; + + public StatusIDEditor( AliasID aliasID ) + { + super( aliasID ); + + initGUI(); + + setComponent( aliasID ); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + + add( new JLabel( "Status:" ) ); + + MaskFormatter formatter = null; + + try + { + //Mask: 3 digits + formatter = new MaskFormatter( "###" ); + } + catch( Exception e ) + { + //Do nothing, the mask was invalid + } + + mTextField = new JFormattedTextField( formatter ); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); + + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public StatusID getStatusID() + { + if( getComponent() instanceof StatusID ) + { + return (StatusID)getComponent(); + } + + return null; + } + + @Override + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; + + StatusID statusID = getStatusID(); + + if( statusID != null ) + { + mTextField.setText( String.valueOf( statusID.getStatus() ) ); + } + + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + StatusID statusID = getStatusID(); + + if( statusID != null ) + { + int status = 0; + + try + { + status = Integer.valueOf( mTextField.getText() ); + } + catch( Exception e ) + { + //Do nothing, we couldn't parse the value + } + + statusID.setStatus( status ); + } + + setModified( false ); + } +} diff --git a/src/alias/id/fleetsync/StatusIDNode.java b/src/alias/id/status/StatusIDNode.java similarity index 97% rename from src/alias/id/fleetsync/StatusIDNode.java rename to src/alias/id/status/StatusIDNode.java index 49aaeb83..19586a5f 100644 --- a/src/alias/id/fleetsync/StatusIDNode.java +++ b/src/alias/id/status/StatusIDNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.id.fleetsync; +package alias.id.status; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -41,7 +41,7 @@ public class StatusIDNode extends AliasIDNode @Override public JPanel getEditor() { - return new StatusIDEditor( this ); + return null; } public StatusID getStatusID() diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index 1feda866..4bae20ac 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -17,99 +17,87 @@ ******************************************************************************/ package alias.id.talkgroup; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; +import alias.AliasID; +import alias.ComponentEditor; -public class TalkgroupIDEditor extends JPanel implements ActionListener +public class TalkgroupIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private TalkgroupIDNode mTalkgroupIDNode; - - private JTextField mTextTalkgroupID; - - private String mHelpText = "Enter a formatted talkgroup identifier.\n\n" - + "P25 Talkgroup Format: hexadecimal value, 4 characters for " - + "talkgroup or 6 characters for a mobile radio\n\n" - + "LTR Talkgroup Format: A-HH-TTT (A=Area, H=Home Repeater, " - + "T=Talkgroup)\n\n" - + "Passport Talkgroup Format: xxxxx (up to 5-digit number)\n\n" - + "Wildcard: use one or more asterisks (*) for any talkgroup digits."; - public TalkgroupIDEditor( TalkgroupIDNode talkgroupIDNode ) + private static final String HELP_TEXT = "Enter a formatted talkgroup identifier.\n" + + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)\n" + + "LTR: A-HH-TTT (A=Area H=Home T=Talkgroup)\n" + + "Passport: xxxxx (up to 5-digit number)\n\n" + + "Wildcard: use an asterisk (*) in place of each talkgroup digit"; + + private JTextField mTextField; + + public TalkgroupIDEditor( AliasID aliasID ) { - mTalkgroupIDNode = talkgroupIDNode; + super( aliasID ); initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][grow]" ) ); - - add( new JLabel( "Talkgroup ID" ), "span,align center" ); - - add( new JLabel( "TGID:" ) ); - mTextTalkgroupID = new JTextField(); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); - mTextTalkgroupID.setText( mTalkgroupIDNode.getTalkgroupID().getTalkgroup() ); + add( new JLabel( "Talkgroup:" ) ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); - add( mTextTalkgroupID, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( TalkgroupIDEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( TalkgroupIDEditor.this ); - add( btnReset, "growx,push" ); - - JTextArea helpText = new JTextArea( mHelpText ); - helpText.setBackground( getBackground() ); + JTextArea helpText = new JTextArea( HELP_TEXT ); helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); add( helpText, "span,grow,push" ); } + + public TalkgroupID getTalkgroupID() + { + if( getComponent() instanceof TalkgroupID ) + { + return (TalkgroupID)getComponent(); + } + + return null; + } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + TalkgroupID talkgroup = getTalkgroupID(); + + if( talkgroup != null ) { - String tgid = mTextTalkgroupID.getText(); - - if( tgid != null ) - { - mTalkgroupIDNode.getTalkgroupID().setTalkgroup( tgid ); - - ((ConfigurableNode)mTalkgroupIDNode.getParent()).sort(); + mTextField.setText( talkgroup.getTalkgroup() ); + } + + setModified( false ); + + repaint(); + } - mTalkgroupIDNode.save(); - - mTalkgroupIDNode.show(); - } - else - { - JOptionPane.showMessageDialog( TalkgroupIDEditor.this, - "Please enter a talkgroup ID" ); - } - } - else if( command.contentEquals( "Reset" ) ) + @Override + public void save() + { + TalkgroupID talkgroup = getTalkgroupID(); + + if( talkgroup != null ) { - mTextTalkgroupID.setText( - mTalkgroupIDNode.getTalkgroupID().getTalkgroup() ); + talkgroup.setTalkgroup( mTextField.getText() ); } - mTalkgroupIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/talkgroup/TalkgroupIDNode.java b/src/alias/id/talkgroup/TalkgroupIDNode.java index 261adc75..1a11cd08 100644 --- a/src/alias/id/talkgroup/TalkgroupIDNode.java +++ b/src/alias/id/talkgroup/TalkgroupIDNode.java @@ -41,7 +41,7 @@ public class TalkgroupIDNode extends AliasIDNode @Override public JPanel getEditor() { - return new TalkgroupIDEditor( this ); + return null; } public TalkgroupID getTalkgroupID() diff --git a/src/alias/id/uniqueID/UniqueIDEditor.java b/src/alias/id/uniqueID/UniqueIDEditor.java index 7b46f176..349f66b6 100644 --- a/src/alias/id/uniqueID/UniqueIDEditor.java +++ b/src/alias/id/uniqueID/UniqueIDEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,101 +17,95 @@ ******************************************************************************/ package alias.id.uniqueID; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; +import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; +import alias.AliasID; +import alias.ComponentEditor; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.ConfigurableNode; - -public class UniqueIDEditor extends JPanel implements ActionListener +public class UniqueIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( UniqueIDEditor.class ); - private UniqueIDNode mUniqueIDNode; - - private JLabel mLabelUniqueID; - private JTextField mTextUniqueID; - public UniqueIDEditor( UniqueIDNode uniqueIDNode ) + private static final String HELP_TEXT = "LTR-Net Unique ID (UID) is a system" + + " identifier assigned to each radio in the range 1 - 2097152"; + + private JTextField mTextField; + + public UniqueIDEditor( AliasID aliasID ) { - mUniqueIDNode = uniqueIDNode; + super( aliasID ); initGUI(); + + setComponent( aliasID ); } private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "LTR-Net Radio Unique ID" ), "span,align center" ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); add( new JLabel( "Unique ID:" ) ); - - mTextUniqueID = new JTextField( - String.valueOf( mUniqueIDNode.getUniqueID().getUid() ) ); - add( mTextUniqueID, "growx,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( UniqueIDEditor.this ); - add( btnSave, "growx,push" ); + mTextField = new JTextField(); + mTextField.getDocument().addDocumentListener( this ); + add( mTextField, "growx,push" ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( UniqueIDEditor.this ); - add( btnReset, "growx,push" ); + JTextArea helpText = new JTextArea( HELP_TEXT ); + helpText.setLineWrap( true ); + helpText.setBackground( getBackground() ); + add( helpText, "span,grow,push" ); + } + + public UniqueID getUniqueID() + { + if( getComponent() instanceof UniqueID ) + { + return (UniqueID)getComponent(); + } + + return null; } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); + public void setComponent( AliasID aliasID ) + { + mComponent = aliasID; - if( command.contentEquals( "Save" ) ) + UniqueID uid = getUniqueID(); + + if( uid != null ) { - String uid = mTextUniqueID.getText(); + mTextField.setText( String.valueOf( uid.getUid() ) ); + } + + setModified( false ); + + repaint(); + } + + @Override + public void save() + { + UniqueID uid = getUniqueID(); + + if( uid != null ) + { + int id = 0; - if( uid != null ) + try { - try - { - int uidInt = Integer.parseInt( uid ); - - mUniqueIDNode.getUniqueID().setUid( uidInt ); - - ((ConfigurableNode)mUniqueIDNode.getParent()).sort(); - - mUniqueIDNode.save(); - - mUniqueIDNode.show(); - } - catch( Exception ex ) - { - mLog.error( "UniqueIDEditor - exception trying to parse " + - "int from uid [" + uid + "]", ex ); - } + id = Integer.parseInt( mTextField.getText() ); } - else + catch( Exception e ) { - JOptionPane.showMessageDialog( UniqueIDEditor.this, - "Please enter a site number and unique ID" ); + //Do nothing, we couldn't parse the value } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextUniqueID.setText( - String.valueOf( mUniqueIDNode.getUniqueID().getUid() ) ); + + uid.setUid( id ); } - mUniqueIDNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/id/uniqueID/UniqueIDNode.java b/src/alias/id/uniqueID/UniqueIDNode.java index ebb92f4f..bdf43be9 100644 --- a/src/alias/id/uniqueID/UniqueIDNode.java +++ b/src/alias/id/uniqueID/UniqueIDNode.java @@ -41,7 +41,7 @@ public class UniqueIDNode extends AliasIDNode @Override public JPanel getEditor() { - return new UniqueIDEditor( this ); + return null; } public UniqueID getUniqueID() diff --git a/src/alias/priority/PriorityEditor.java b/src/alias/priority/PriorityEditor.java deleted file mode 100644 index 1028ae74..00000000 --- a/src/alias/priority/PriorityEditor.java +++ /dev/null @@ -1,191 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ -package alias.priority; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JSlider; -import javax.swing.JTextArea; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.ConfigurableNode; - -public class PriorityEditor extends JPanel implements ActionListener -{ - private final static Logger mLog = LoggerFactory.getLogger( PriorityEditor.class ); - - public static final String DO_NOT_MONITOR = "Do Not Monitor"; - public static final String SAVE = "Save"; - public static final String RESET = "Reset"; - - private static final long serialVersionUID = 1L; - private PriorityNode mPriorityNode; - private JCheckBox mDoNotMonitorCheckBox = new JCheckBox( DO_NOT_MONITOR ); - private JSlider mPrioritySlider; - private JLabel mPrioritySliderLabel; - - public PriorityEditor( PriorityNode priorityNode ) - { - mPriorityNode = priorityNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - add( new JLabel( "Priority" ), "span,align center" ); - - boolean doNotMonitor = mPriorityNode.getPriority().isDoNotMonitor(); - - mDoNotMonitorCheckBox.addActionListener( this ); - - if( doNotMonitor ) - { - mDoNotMonitorCheckBox.setSelected( true ); - } - - add( mDoNotMonitorCheckBox, "span,align center" ); - - int priority = mPriorityNode.getPriority().getPriority(); - - /* Adjust the displayable priority value to within the min/max bounds - * so we don't get an error in the jslider control. If the actual - * priority is -1 (ie do not follow), the controls will be updated - * to reflect that state */ - if( priority < Priority.MIN_PRIORITY ) - { - priority = Priority.DEFAULT_PRIORITY; - } - else if( priority > Priority.MAX_PRIORITY ) - { - priority = Priority.DEFAULT_PRIORITY; - } - - mPrioritySlider = new JSlider( JSlider.HORIZONTAL, - Priority.MIN_PRIORITY, - Priority.MAX_PRIORITY, - priority ); - - mPrioritySlider.setMajorTickSpacing( 20 ); - mPrioritySlider.setMinorTickSpacing( 5 ); - mPrioritySlider.setPaintTicks( true ); - - mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); - mPrioritySlider.setPaintLabels( true ); - - mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); - - mPrioritySlider.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); - } - } ); - - if( doNotMonitor ) - { - mPrioritySlider.setEnabled( false ); - mPrioritySliderLabel.setEnabled( false ); - } - - add( mPrioritySliderLabel ); - add( mPrioritySlider, "wrap,grow" ); - - JButton btnSave = new JButton( SAVE ); - btnSave.addActionListener( PriorityEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( RESET ); - btnReset.addActionListener( PriorityEditor.this ); - add( btnReset, "growx,push" ); - - StringBuilder sb = new StringBuilder(); - sb.append( "Select a value for audio priority within the range " ); - sb.append( Priority.MIN_PRIORITY ); - sb.append( " and " ); - sb.append( Priority.MAX_PRIORITY ); - sb.append( ". Lower values indicate higher priority levels." ); - - JTextArea helpText = new JTextArea( sb.toString() ); - helpText.setLineWrap( true ); - add( helpText, "span,grow,push" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( SAVE ) ) - { - if( mDoNotMonitorCheckBox.isSelected() ) - { - mPriorityNode.getPriority().setPriority( Priority.DO_NOT_MONITOR ); - } - else - { - mPriorityNode.getPriority().setPriority( mPrioritySlider.getValue() ); - } - - ((ConfigurableNode)mPriorityNode.getParent()).sort(); - - mPriorityNode.save(); - - mPriorityNode.show(); - } - else if( command.contentEquals( RESET ) ) - { - mPrioritySlider.setValue( mPriorityNode.getPriority().getPriority() ); - } - else if( command.contentEquals( DO_NOT_MONITOR ) ) - { - if( mDoNotMonitorCheckBox.isSelected() ) - { - mPriorityNode.getPriority().setPriority( Priority.DO_NOT_MONITOR ); - mPrioritySlider.setEnabled( false ); - mPrioritySliderLabel.setEnabled( false ); - } - else - { - mPriorityNode.getPriority().setPriority( Priority.DEFAULT_PRIORITY ); - mPrioritySlider.setEnabled( true ); - mPrioritySliderLabel.setEnabled( true ); - } - - mPriorityNode.save(); - mPriorityNode.show(); - } - - mPriorityNode.refresh(); - } -} diff --git a/src/alias/record/NonRecordableEditor.java b/src/alias/record/NonRecordableEditor.java deleted file mode 100644 index e9ab06e3..00000000 --- a/src/alias/record/NonRecordableEditor.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.record; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JButton; -import javax.swing.JPanel; -import javax.swing.JTextArea; - -import net.miginfocom.swing.MigLayout; - -public class NonRecordableEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - - private NonRecordableNode mNode; - - public NonRecordableEditor( NonRecordableNode node ) - { - mNode = node; - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill", "[left]", "[grow]" ) ); - - JTextArea description = new JTextArea( "Non-Recordable. Any audio where" - + " this alias is a participant in the call will not be recorded." ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( NonRecordableEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( NonRecordableEditor.this ); - add( btnReset, "growx,push" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - mNode.save(); - mNode.show(); - } - - mNode.refresh(); - } -} diff --git a/src/audio/metadata/AudioMetadata.java b/src/audio/metadata/AudioMetadata.java index ad498288..d4b8cae0 100644 --- a/src/audio/metadata/AudioMetadata.java +++ b/src/audio/metadata/AudioMetadata.java @@ -28,7 +28,7 @@ import org.slf4j.LoggerFactory; import sample.Listener; import alias.Alias; -import alias.priority.Priority; +import alias.id.priority.Priority; public class AudioMetadata implements Listener { diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index 5455d7d5..ff4eae65 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -22,7 +22,7 @@ import source.config.SourceConfigTuner; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; import alias.Alias; -import alias.priority.Priority; +import alias.id.priority.Priority; import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; From a807ff5c07cea61924a83c39243dab57d24d8fa5 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 22 Feb 2016 20:47:40 -0500 Subject: [PATCH 33/74] Resolves issue with RTL2832 tuner where we attempt to cancel transfers that have not yet been initialized. --- src/source/tuner/rtl/RTL2832TunerController.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index e972c1d0..6f37cda3 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -1525,7 +1525,14 @@ public abstract class RTL2832TunerController extends TunerController { for( Transfer transfer: mTransfers ) { - LibUsb.cancelTransfer( transfer ); + try + { + LibUsb.cancelTransfer( transfer ); + } + catch( IllegalStateException ise ) + { + //Do nothing, the transfer pointer wasn't initialized + } } ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); From 62851351a443bedcbf8121b7c348603e861c274f Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 27 Feb 2016 08:58:47 -0500 Subject: [PATCH 34/74] Merges master 399d164 - default tuner configurations issue #57 --- src/settings/SettingsManager.java | 5 ----- src/source/tuner/TunerManager.java | 8 +++----- src/source/tuner/rtl/r820t/R820TTunerConfiguration.java | 2 +- 3 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index ee25bd78..3cac8abe 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -564,11 +564,6 @@ public class SettingsManager ArrayList configs = getSettings() .getTunerConfigurations( type ); - if( configs.isEmpty() ) - { - configs.add( addNewTunerConfiguration( type, "Default" ) ); - } - return configs; } diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index 29b87fb8..759bf7d6 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -547,16 +547,14 @@ public class TunerManager } } } - else - { - config = configs.get( 0 ); - } //If we're still null at this point, create a default config if( config == null ) { + String name = "Default-" + tuner.getUniqueID(); + config = mSettingsManager.addNewTunerConfiguration( - tuner.getTunerType(), "Default" ); + tuner.getTunerType(), name ); } //Persist the config as the selected tuner configuration. The method diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java index acca6fba..8ecc7795 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java @@ -34,7 +34,7 @@ public class R820TTunerConfiguration extends TunerConfiguration private R820TLNAGain mLNAGain = R820TLNAGain.GAIN_222; private R820TVGAGain mVGAGain = R820TVGAGain.GAIN_210; private double mFrequencyCorrection = 0.0d; - private SampleRate mSampleRate = SampleRate.RATE_1_920MHZ; + private SampleRate mSampleRate = SampleRate.RATE_2_400MHZ; /** * Default constructor for JAXB From 8d10b502ab9f59d09fe0b224e98249ddd6927e5c Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 27 Feb 2016 09:01:07 -0500 Subject: [PATCH 35/74] Merges master ca8406b - DFT processor queue backing up --- src/spectrum/DFTProcessor.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index 7900626f..b055fd0f 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -20,6 +20,7 @@ package spectrum; import java.util.Arrays; import java.util.Iterator; import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -56,9 +57,13 @@ public class DFTProcessor implements Listener, private CopyOnWriteArrayList mListeners = new CopyOnWriteArrayList(); - private ArrayBlockingQueue mQueue = - new ArrayBlockingQueue( 200 ); - + //Fixed size sample queue. We set the size at 12 based on the highest + //sample rate tuner (10 MHz) producing 153 frames per second and the lowest + //DFT processing frame rate of 12 frames per second. This means that the + //maximum size needed is 153 / 12 = 11 frames per DFT processing iteration + //so we set it at 12. + private BlockingQueue mQueue = new ArrayBlockingQueue<>( 12 ); + private ScheduledExecutorService mScheduler = Executors .newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) ); From 15716d12706db2dd434c206d1ab4aea8f99938da Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 27 Feb 2016 09:02:17 -0500 Subject: [PATCH 36/74] merges master 455e452 - removes logging from dft processor about buffer overflows --- src/spectrum/DFTProcessor.java | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java index b055fd0f..a90c432d 100644 --- a/src/spectrum/DFTProcessor.java +++ b/src/spectrum/DFTProcessor.java @@ -251,15 +251,7 @@ public class DFTProcessor implements Listener, @Override public void receive( ComplexBuffer sampleBuffer ) { - if( !mQueue.offer( sampleBuffer ) ) - { - mLog.error( "DFTProcessor - [" + mSampleType.toString() - + "] queue is full, purging queue, " - + "samples[" + sampleBuffer + "]" ); - - mQueue.clear(); - mQueue.offer( sampleBuffer ); - } + mQueue.offer( sampleBuffer ); } private void getNextBuffer() From ece555dead9e86875a1234c442c5f276be95994b Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 27 Feb 2016 09:04:13 -0500 Subject: [PATCH 37/74] Merges master 1550c62 - channel state panel null checking --- src/module/decode/state/ChannelPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/module/decode/state/ChannelPanel.java b/src/module/decode/state/ChannelPanel.java index 0d88f826..5dee9814 100644 --- a/src/module/decode/state/ChannelPanel.java +++ b/src/module/decode/state/ChannelPanel.java @@ -227,7 +227,8 @@ public class ChannelPanel extends JPanel switch( changedAttribute ) { case CHANNEL_STATE: - if( mChannelState.getState() != null ) + if( mChannelState != null && + mChannelState.getState() != null ) { mStateLabel.setText( mChannelState.getState().getDisplayValue() ); } From 63cea60c42e379f27876593a04a21c8664c83a56 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 27 Feb 2016 09:45:47 -0500 Subject: [PATCH 38/74] Merges master CF2C0CA, EEAD22F, 5F2D8C8, 22D3871, and 6F2A7E8 - updates libusb transfer processing and shutdown/cancel management of transfers in progress. --- .../tuner/airspy/AirspyTunerController.java | 235 ++++++++++-------- .../tuner/hackrf/HackRFTunerController.java | 216 ++++++++-------- .../tuner/rtl/RTL2832TunerController.java | 234 ++++++++--------- 3 files changed, 363 insertions(+), 322 deletions(-) diff --git a/src/source/tuner/airspy/AirspyTunerController.java b/src/source/tuner/airspy/AirspyTunerController.java index 755465d0..fb82784d 100644 --- a/src/source/tuner/airspy/AirspyTunerController.java +++ b/src/source/tuner/airspy/AirspyTunerController.java @@ -262,6 +262,30 @@ public class AirspyTunerController extends TunerController } } + public static String getTransferStatus( int status ) + { + switch( status ) + { + case 0: + return "TRANSFER COMPLETED (0)"; + case 1: + return "TRANSFER ERROR (1)"; + case 2: + return "TRANSFER TIMED OUT (2)"; + case 3: + return "TRANSFER CANCELLED (3)"; + case 4: + return "TRANSFER STALL (4)"; + case 5: + return "TRANSFER NO DEVICE (5)"; + case 6: + return "TRANSFER OVERFLOW (6)"; + default: + return "UNKNOWN TRANSFER STATUS (" + status + ")"; + } + } + + @Override public void apply( TunerConfiguration config ) throws SourceException { @@ -1192,17 +1216,20 @@ public class AirspyTunerController extends TunerController */ public void addListener( Listener listener ) { - mComplexBufferBroadcaster.addListener( listener ); - - if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) + synchronized ( mComplexBufferBroadcaster ) { - mBufferProcessor = new BufferProcessor(); + mComplexBufferBroadcaster.addListener( listener ); - Thread thread = new Thread( mBufferProcessor ); - thread.setDaemon( true ); - thread.setName( "Airspy Buffer Processor" ); + if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) + { + mBufferProcessor = new BufferProcessor(); - thread.start(); + Thread thread = new Thread( mBufferProcessor ); + thread.setDaemon( true ); + thread.setName( "Airspy Buffer Processor" ); + + thread.start(); + } } } @@ -1212,11 +1239,14 @@ public class AirspyTunerController extends TunerController */ public void removeListener( Listener listener ) { - mComplexBufferBroadcaster.removeListener( listener ); - - if( !mComplexBufferBroadcaster.hasListeners() ) + synchronized ( mComplexBufferBroadcaster ) { - mBufferProcessor.stop(); + mComplexBufferBroadcaster.removeListener( listener ); + + if( !mComplexBufferBroadcaster.hasListeners() ) + { + mBufferProcessor.stop(); + } } } @@ -1227,9 +1257,12 @@ public class AirspyTunerController extends TunerController public class BufferProcessor implements Runnable, TransferCallback { private ScheduledFuture mSampleDispatcherTask; - private CopyOnWriteArrayList mTransfers; + private LinkedTransferQueue mAvailableTransfers; + private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); private AtomicBoolean mRunning = new AtomicBoolean(); - + private ByteBuffer mLibUsbHandlerStatus; + private boolean mCancel = false; + @Override public void run() { @@ -1237,33 +1270,64 @@ public class AirspyTunerController extends TunerController { prepareTransfers(); - for( Transfer transfer: mTransfers ) - { - int result = LibUsb.submitTransfer( transfer ); - - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error submitting transfer [" + - LibUsb.errorName( result ) + "]" ); - break; - } - } - mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), 20, TimeUnit.MILLISECONDS ); - while( mRunning.get() ) + mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 ); + + List transfers = new ArrayList<>(); + + mCancel = false; + + while( mRunning.get() ) { - ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); + mAvailableTransfers.drainTo( transfers ); + + for( Transfer transfer: transfers ) + { + int result = LibUsb.submitTransfer( transfer ); + + if( result == LibUsb.SUCCESS ) + { + mTransfersInProgress.add( transfer ); + } + else + { + mLog.error( "error submitting transfer [" + + LibUsb.errorName( result ) + "]" ); + } + } int result = LibUsb.handleEventsTimeoutCompleted( - null, USB_TIMEOUT_MS, completed.asIntBuffer() ); + null, USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) { mLog.error( "error handling events for libusb" ); } + + transfers.clear(); + + mLibUsbHandlerStatus.rewind(); + } + + if( mCancel ) + { + for( Transfer transfer: mTransfersInProgress ) + { + LibUsb.cancelTransfer( transfer ); + } + + int result = LibUsb.handleEventsTimeoutCompleted( null, + USB_TIMEOUT_MS, mLibUsbHandlerStatus.asIntBuffer() ); + + if( result != LibUsb.SUCCESS ) + { + mLog.error( "error handling events for libusb during cancel" ); + } + + mLibUsbHandlerStatus.rewind(); } } } @@ -1273,10 +1337,10 @@ public class AirspyTunerController extends TunerController */ public void stop() { + mCancel = true; + if( mRunning.compareAndSet( true, false ) ) { - cancel(); - if( mSampleDispatcherTask != null ) { mSampleDispatcherTask.cancel( true ); @@ -1296,66 +1360,39 @@ public class AirspyTunerController extends TunerController @Override public void processTransfer( Transfer transfer ) { - if( transfer.status() == LibUsb.TRANSFER_COMPLETED ) - { - ByteBuffer buffer = transfer.buffer(); - - byte[] data = new byte[ transfer.actualLength() ]; - - buffer.get( data ); - - buffer.rewind(); - - mFilledBuffers.add( data ); - - if( !isRunning() ) - { - LibUsb.cancelTransfer( transfer ); - } - } + mTransfersInProgress.remove( transfer ); switch( transfer.status() ) { case LibUsb.TRANSFER_COMPLETED: - /* resubmit the transfer */ - int result = LibUsb.submitTransfer( transfer ); - - if( result != LibUsb.SUCCESS ) + case LibUsb.TRANSFER_STALL: + if( transfer.actualLength() > 0 ) { - mLog.error( "couldn't resubmit buffer transfer to tuner" ); - LibUsb.freeTransfer( transfer ); - mTransfers.remove( transfer ); + ByteBuffer buffer = transfer.buffer(); + + byte[] data = new byte[ transfer.actualLength() ]; + + buffer.get( data ); + + buffer.rewind(); + + if( isRunning() ) + { + mFilledBuffers.add( data ); + } } break; case LibUsb.TRANSFER_CANCELLED: - /* free the transfer and remove it */ - LibUsb.freeTransfer( transfer ); - mTransfers.remove( transfer ); break; default: /* unexpected error */ - mLog.error( "transfer error" ); + mLog.error( "transfer error [" + + getTransferStatus( transfer.status() ) + + "] transferred actual: " + transfer.actualLength() ); } - } - - private void cancel() - { - for( Transfer transfer: mTransfers ) - { - LibUsb.cancelTransfer( transfer ); - } - - ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); - - int result = LibUsb.handleEventsTimeoutCompleted( - null, USB_TIMEOUT_MS, completed.asIntBuffer() ); - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error handling usb events during cancel [" + - LibUsb.errorName( result ) + "]" ); - } - } + mAvailableTransfers.add( transfer ); + } /** * Prepares (allocates) a set of transfer buffers for use in @@ -1363,30 +1400,28 @@ public class AirspyTunerController extends TunerController */ private void prepareTransfers() throws LibUsbException { - mTransfers = new CopyOnWriteArrayList(); - - for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ ) + if( mAvailableTransfers == null ) { - Transfer transfer = LibUsb.allocTransfer(); + mAvailableTransfers = new LinkedTransferQueue<>(); + + for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ ) + { + Transfer transfer = LibUsb.allocTransfer(); - if( transfer == null ) - { - throw new LibUsbException( "couldn't allocate transfer", - LibUsb.ERROR_NO_MEM ); - } - - final ByteBuffer buffer = - ByteBuffer.allocateDirect( mBufferSize ); - - LibUsb.fillBulkTransfer( transfer, - mDeviceHandle, - USB_ENDPOINT, - buffer, - BufferProcessor.this, - "Buffer #" + x, - USB_TIMEOUT_MS ); - - mTransfers.add( transfer ); + if( transfer == null ) + { + throw new LibUsbException( "couldn't allocate transfer", + LibUsb.ERROR_NO_MEM ); + } + + final ByteBuffer buffer = + ByteBuffer.allocateDirect( mBufferSize ); + + LibUsb.fillBulkTransfer( transfer, mDeviceHandle, USB_ENDPOINT, + buffer, BufferProcessor.this, "Buffer", USB_TIMEOUT_MS ); + + mAvailableTransfers.add( transfer ); + } } } } diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index bdc3bce2..cf4146d1 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -56,6 +56,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; @@ -91,7 +92,7 @@ public class HackRFTunerController extends TunerController private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerController.class ); - public final static long TIMEOUT_US = 1000000l; //uSeconds + public final static long USB_TIMEOUT_US = 1000000l; //uSeconds public static final byte USB_ENDPOINT = (byte)0x81; public static final byte USB_INTERFACE = (byte)0x0; @@ -394,7 +395,7 @@ public class HackRFTunerController extends TunerController (short)value, (short)index, buffer, - TIMEOUT_US ); + USB_TIMEOUT_US ); if( transferred < 0 ) { @@ -467,7 +468,7 @@ public class HackRFTunerController extends TunerController (short)value, (short)index, buffer, - TIMEOUT_US ); + USB_TIMEOUT_US ); if( transferred < 0 ) { @@ -886,17 +887,20 @@ public class HackRFTunerController extends TunerController */ public void addListener( Listener listener ) { - mComplexBufferBroadcaster.addListener( listener ); - - if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) + synchronized ( mComplexBufferBroadcaster ) { - mBufferProcessor = new BufferProcessor(); + mComplexBufferBroadcaster.addListener( listener ); - Thread thread = new Thread( mBufferProcessor ); - thread.setDaemon( true ); - thread.setName( "HackRF Sample Processor" ); + if( mBufferProcessor == null || !mBufferProcessor.isRunning() ) + { + mBufferProcessor = new BufferProcessor(); - thread.start(); + Thread thread = new Thread( mBufferProcessor ); + thread.setDaemon( true ); + thread.setName( "HackRF Sample Processor" ); + + thread.start(); + } } } @@ -906,11 +910,14 @@ public class HackRFTunerController extends TunerController */ public void removeListener( Listener listener ) { - mComplexBufferBroadcaster.removeListener( listener ); - - if( !mComplexBufferBroadcaster.hasListeners() ) + synchronized ( mComplexBufferBroadcaster ) { - mBufferProcessor.stop(); + mComplexBufferBroadcaster.removeListener( listener ); + + if( !mComplexBufferBroadcaster.hasListeners() ) + { + mBufferProcessor.stop(); + } } } @@ -921,46 +928,78 @@ public class HackRFTunerController extends TunerController public class BufferProcessor implements Runnable, TransferCallback { private ScheduledFuture mSampleDispatcherTask; - private CopyOnWriteArrayList mTransfers; private AtomicBoolean mRunning = new AtomicBoolean(); - + private LinkedTransferQueue mAvailableTransfers; + private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); + private ByteBuffer mLibUsbHandlerStatus; + private boolean mCancel = false; + @Override public void run() { if( mRunning.compareAndSet( false, true ) ) { - mLog.info( "starting HackRF sample fetch thread" ); - prepareTransfers(); - for( Transfer transfer: mTransfers ) - { - int result = LibUsb.submitTransfer( transfer ); - - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error submitting transfer [" + - LibUsb.errorName( result ) + "]" ); - break; - } - } - mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( ThreadType.SOURCE_SAMPLE_PROCESSING, new BufferDispatcher(), 20, TimeUnit.MILLISECONDS ); + mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 ); + + List transfers = new ArrayList<>(); + + mCancel = false; + while( mRunning.get() ) { - ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); + mAvailableTransfers.drainTo( transfers ); + + for( Transfer transfer: transfers ) + { + int result = LibUsb.submitTransfer( transfer ); + + if( result == LibUsb.SUCCESS ) + { + mTransfersInProgress.add( transfer ); + } + else + { + mLog.error( "error submitting transfer [" + + LibUsb.errorName( result ) + "]" ); + } + } int result = LibUsb.handleEventsTimeoutCompleted( - null, TIMEOUT_US, completed.asIntBuffer() ); + null, USB_TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) { mLog.error( "error handling events for libusb" ); } + + transfers.clear(); + + mLibUsbHandlerStatus.rewind(); } + + if( mCancel ) + { + for( Transfer transfer: mTransfersInProgress ) + { + LibUsb.cancelTransfer( transfer ); + } + + int result = LibUsb.handleEventsTimeoutCompleted( null, + USB_TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); + + if( result != LibUsb.SUCCESS ) + { + mLog.error( "error cancelling transfers in progress" ); + } + + mLibUsbHandlerStatus.rewind(); + } } } @@ -969,12 +1008,10 @@ public class HackRFTunerController extends TunerController */ public void stop() { + mCancel = true; + if( mRunning.compareAndSet( true, false ) ) { - mLog.info( "stopping HackRF sample fetch thread" ); - - cancel(); - if( mSampleDispatcherTask != null ) { mSampleDispatcherTask.cancel( true ); @@ -994,41 +1031,29 @@ public class HackRFTunerController extends TunerController @Override public void processTransfer( Transfer transfer ) { - if( transfer.status() == LibUsb.TRANSFER_COMPLETED ) - { - ByteBuffer buffer = transfer.buffer(); - - byte[] data = new byte[ transfer.actualLength() ]; - - buffer.get( data ); - - buffer.rewind(); - - mFilledBuffers.add( data ); - - if( !isRunning() ) - { - LibUsb.cancelTransfer( transfer ); - } - } + mTransfersInProgress.remove( transfer ); switch( transfer.status() ) { case LibUsb.TRANSFER_COMPLETED: - /* resubmit the transfer */ - int result = LibUsb.submitTransfer( transfer ); - - if( result != LibUsb.SUCCESS ) + case LibUsb.TRANSFER_STALL: + if( transfer.actualLength() > 0 ) { - mLog.error( "couldn't resubmit buffer transfer to tuner" ); - LibUsb.freeTransfer( transfer ); - mTransfers.remove( transfer ); + ByteBuffer buffer = transfer.buffer(); + + byte[] data = new byte[ transfer.actualLength() ]; + + buffer.get( data ); + + buffer.rewind(); + + if( isRunning() ) + { + mFilledBuffers.add( data ); + } } break; case LibUsb.TRANSFER_CANCELLED: - /* free the transfer and remove it */ - LibUsb.freeTransfer( transfer ); - mTransfers.remove( transfer ); break; default: /* unexpected error */ @@ -1036,26 +1061,9 @@ public class HackRFTunerController extends TunerController getTransferStatus( transfer.status() ) + "] transferred actual: " + transfer.actualLength() ); } - } - - private void cancel() - { - for( Transfer transfer: mTransfers ) - { - LibUsb.cancelTransfer( transfer ); - } - - ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); - - int result = LibUsb.handleEventsTimeoutCompleted( - null, TIMEOUT_US, completed.asIntBuffer() ); - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error handling usb events during cancel [" + - LibUsb.errorName( result ) + "]" ); - } - } + mAvailableTransfers.add( transfer ); + } /** * Prepares (allocates) a set of transfer buffers for use in @@ -1063,30 +1071,28 @@ public class HackRFTunerController extends TunerController */ private void prepareTransfers() throws LibUsbException { - mTransfers = new CopyOnWriteArrayList(); - - for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ ) + if( mAvailableTransfers == null ) { - Transfer transfer = LibUsb.allocTransfer(); + mAvailableTransfers = new LinkedTransferQueue<>(); + + for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ ) + { + Transfer transfer = LibUsb.allocTransfer(); - if( transfer == null ) - { - throw new LibUsbException( "couldn't allocate transfer", - LibUsb.ERROR_NO_MEM ); - } - - final ByteBuffer buffer = - ByteBuffer.allocateDirect( mBufferSize ); - - LibUsb.fillBulkTransfer( transfer, - mDeviceHandle, - USB_ENDPOINT, - buffer, - BufferProcessor.this, - "Buffer #" + x, - TIMEOUT_US ); - - mTransfers.add( transfer ); + if( transfer == null ) + { + throw new LibUsbException( "couldn't allocate transfer", + LibUsb.ERROR_NO_MEM ); + } + + final ByteBuffer buffer = + ByteBuffer.allocateDirect( mBufferSize ); + + LibUsb.fillBulkTransfer( transfer, mDeviceHandle,USB_ENDPOINT, + buffer, BufferProcessor.this, "Buffer", USB_TIMEOUT_US ); + + mAvailableTransfers.add( transfer ); + } } } } diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index 6f37cda3..802bd15b 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -27,6 +27,7 @@ import java.nio.charset.Charset; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.LinkedTransferQueue; import java.util.concurrent.ScheduledFuture; @@ -71,7 +72,7 @@ public abstract class RTL2832TunerController extends TunerController public final static int TWO_TO_22_POWER = 4194304; public final static byte USB_INTERFACE = (byte)0x0; - public final static byte BULK_ENDPOINT_IN = (byte)0x81; + public final static byte USB_BULK_ENDPOINT = (byte)0x81; public final static byte CONTROL_ENDPOINT_IN = (byte)( LibUsb.ENDPOINT_IN | LibUsb.REQUEST_TYPE_VENDOR ); public final static byte CONTROL_ENDPOINT_OUT = @@ -1340,21 +1341,24 @@ public abstract class RTL2832TunerController extends TunerController */ public void addListener( Listener listener ) { - mComplexBufferBroadcaster.addListener( listener ); + synchronized( mComplexBufferBroadcaster ) + { + mComplexBufferBroadcaster.addListener( listener ); - if( mBufferProcessor == null ) - { - mBufferProcessor = new BufferProcessor( mThreadPoolManager ); - } - - if( !mBufferProcessor.isRunning() ) - { - Thread thread = new Thread( mBufferProcessor ); - thread.setDaemon( true ); - thread.setName( "RTL2832 Sample Processor" ); + if( mBufferProcessor == null ) + { + mBufferProcessor = new BufferProcessor( mThreadPoolManager ); + } + + if( !mBufferProcessor.isRunning() ) + { + Thread thread = new Thread( mBufferProcessor ); + thread.setDaemon( true ); + thread.setName( "RTL2832 Sample Processor" ); - thread.start(); - } + thread.start(); + } + } } /** @@ -1363,12 +1367,15 @@ public abstract class RTL2832TunerController extends TunerController */ public void removeListener( Listener listener ) { - mComplexBufferBroadcaster.removeListener( listener ); + synchronized( mComplexBufferBroadcaster ) + { + mComplexBufferBroadcaster.removeListener( listener ); - if( !mComplexBufferBroadcaster.hasListeners() ) - { - mBufferProcessor.stop(); - } + if( !mComplexBufferBroadcaster.hasListeners() ) + { + mBufferProcessor.stop(); + } + } } /** @@ -1378,10 +1385,13 @@ public abstract class RTL2832TunerController extends TunerController public class BufferProcessor implements Runnable, TransferCallback { private ScheduledFuture mSampleDispatcherTask; - private CopyOnWriteArrayList mTransfers; + private LinkedTransferQueue mAvailableTransfers; + private LinkedTransferQueue mTransfersInProgress = new LinkedTransferQueue<>(); private AtomicBoolean mRunning = new AtomicBoolean(); private ThreadPoolManager mThreadPoolManager; - + private ByteBuffer mLibUsbHandlerStatus; + private boolean mCancel = false; + public BufferProcessor( ThreadPoolManager manager ) { mThreadPoolManager = manager; @@ -1407,18 +1417,6 @@ public abstract class RTL2832TunerController extends TunerController prepareTransfers(); - for( Transfer transfer: mTransfers ) - { - int result = LibUsb.submitTransfer( transfer ); - - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error submitting transfer [" + - LibUsb.errorName( result ) + "]" ); - break; - } - } - try { mSampleDispatcherTask = mThreadPoolManager.scheduleFixedRate( @@ -1430,19 +1428,61 @@ public abstract class RTL2832TunerController extends TunerController mLog.error( "NPE! = tpm null: " + ( mThreadPoolManager == null ), npe ); } - + mLibUsbHandlerStatus = ByteBuffer.allocateDirect( 4 ); + + List transfers = new ArrayList<>(); + + mCancel = false; + while( mRunning.get() ) { - ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); - + mAvailableTransfers.drainTo( transfers ); + + for( Transfer transfer: transfers ) + { + int result = LibUsb.submitTransfer( transfer ); + + if( result == LibUsb.SUCCESS ) + { + mTransfersInProgress.add( transfer ); + } + else + { + mLog.error( "Error submitting transfer [" + + LibUsb.errorName( result ) + "]" ); + } + } + int result = LibUsb.handleEventsTimeoutCompleted( - null, TIMEOUT_US, completed.asIntBuffer() ); + null, TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); if( result != LibUsb.SUCCESS ) { mLog.error( "error handling events for libusb" ); } + + transfers.clear(); + + mLibUsbHandlerStatus.rewind(); } + + if( mCancel ) + { + for( Transfer transfer: mTransfersInProgress ) + { + LibUsb.cancelTransfer( transfer ); + } + + int result = LibUsb.handleEventsTimeoutCompleted( null, + TIMEOUT_US, mLibUsbHandlerStatus.asIntBuffer() ); + + if( result != LibUsb.SUCCESS ) + { + mLog.error( "error handling events for libusb during cancel" ); + } + + mLibUsbHandlerStatus.rewind(); + } } } @@ -1451,10 +1491,10 @@ public abstract class RTL2832TunerController extends TunerController */ public void stop() { + mCancel = true; + if( mRunning.compareAndSet( true, false ) ) { - cancel(); - if( mSampleDispatcherTask != null ) { mThreadPoolManager.cancel( mSampleDispatcherTask ); @@ -1473,45 +1513,31 @@ public abstract class RTL2832TunerController extends TunerController } @Override - public void processTransfer( Transfer transfer ) + public void processTransfer( Transfer transfer ) { - if( transfer.status() == LibUsb.TRANSFER_COMPLETED ) - { - ByteBuffer buffer = transfer.buffer(); - - byte[] data = new byte[ transfer.actualLength() ]; - - buffer.get( data ); - - buffer.rewind(); - - mFilledBuffers.add( data ); - - mSampleCounter.addAndGet( transfer.actualLength() ); - - if( !isRunning() ) - { - LibUsb.cancelTransfer( transfer ); - } - } + mTransfersInProgress.remove( transfer ); switch( transfer.status() ) { case LibUsb.TRANSFER_COMPLETED: - /* resubmit the transfer */ - int result = LibUsb.submitTransfer( transfer ); - - if( result != LibUsb.SUCCESS ) + case LibUsb.TRANSFER_STALL: + if( transfer.actualLength() > 0 ) { - mLog.error( "couldn't resubmit buffer transfer to tuner" ); - LibUsb.freeTransfer( transfer ); - mTransfers.remove( transfer ); + ByteBuffer buffer = transfer.buffer(); + + byte[] data = new byte[ transfer.actualLength() ]; + + buffer.get( data ); + + buffer.rewind(); + + if( isRunning() ) + { + mFilledBuffers.add( data ); + } } break; case LibUsb.TRANSFER_CANCELLED: - /* free the transfer and remove it */ - LibUsb.freeTransfer( transfer ); - mTransfers.remove( transfer ); break; default: /* unexpected error */ @@ -1519,33 +1545,9 @@ public abstract class RTL2832TunerController extends TunerController getTransferStatus( transfer.status() ) + "] transferred actual: " + transfer.actualLength() ); } - } - - private void cancel() - { - for( Transfer transfer: mTransfers ) - { - try - { - LibUsb.cancelTransfer( transfer ); - } - catch( IllegalStateException ise ) - { - //Do nothing, the transfer pointer wasn't initialized - } - } - - ByteBuffer completed = ByteBuffer.allocateDirect( 4 ); - - int result = LibUsb.handleEventsTimeoutCompleted( - null, TIMEOUT_US, completed.asIntBuffer() ); - if( result != LibUsb.SUCCESS ) - { - mLog.error( "error handling usb events during cancel [" + - LibUsb.errorName( result ) + "]" ); - } - } + mAvailableTransfers.add( transfer ); + } /** * Prepares (allocates) a set of transfer buffers for use in @@ -1553,30 +1555,28 @@ public abstract class RTL2832TunerController extends TunerController */ private void prepareTransfers() throws LibUsbException { - mTransfers = new CopyOnWriteArrayList(); - - for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ ) + if( mAvailableTransfers == null ) { - Transfer transfer = LibUsb.allocTransfer(); + mAvailableTransfers = new LinkedTransferQueue<>(); + + for( int x = 0; x < TRANSFER_BUFFER_POOL_SIZE; x++ ) + { + Transfer transfer = LibUsb.allocTransfer(); - if( transfer == null ) - { - throw new LibUsbException( "couldn't allocate transfer", - LibUsb.ERROR_NO_MEM ); - } - - final ByteBuffer buffer = - ByteBuffer.allocateDirect( mBufferSize ); - - LibUsb.fillBulkTransfer( transfer, - mDeviceHandle, - BULK_ENDPOINT_IN, - buffer, - BufferProcessor.this, - "Buffer #" + x, - TIMEOUT_US ); - - mTransfers.add( transfer ); + if( transfer == null ) + { + throw new LibUsbException( "couldn't allocate transfer", + LibUsb.ERROR_NO_MEM ); + } + + final ByteBuffer buffer = + ByteBuffer.allocateDirect( mBufferSize ); + + LibUsb.fillBulkTransfer( transfer, mDeviceHandle, USB_BULK_ENDPOINT, + buffer, BufferProcessor.this, "Buffer", TIMEOUT_US ); + + mAvailableTransfers.add( transfer ); + } } } } From e31b6adf52d81eea4c9957a2c522bf0e831bd45e Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 28 Feb 2016 10:06:28 -0500 Subject: [PATCH 39/74] Updated help text display for each of the alias identifier editors. --- src/alias/id/esn/ESNEditor.java | 38 +++++++++++----- src/alias/id/fleetsync/FleetsyncIDEditor.java | 34 ++++++++++---- src/alias/id/lojack/LoJackIDEditor.java | 44 ++++++++++++++----- src/alias/id/mdc/MDC1200IDEditor.java | 36 +++++++++++---- src/alias/id/mobileID/MINEditor.java | 36 +++++++++++---- src/alias/id/mpt1327/MPT1327IDEditor.java | 37 ++++++++++++---- .../id/nonrecordable/NonRecordableEditor.java | 37 ++++++++++++---- src/alias/id/priority/PriorityEditor.java | 39 +++++++++++----- src/alias/id/siteID/SiteIDEditor.java | 40 ++++++++++++----- src/alias/id/status/StatusIDEditor.java | 39 +++++++++++----- src/alias/id/talkgroup/TalkgroupIDEditor.java | 41 ++++++++++++----- src/alias/id/uniqueID/UniqueIDEditor.java | 34 ++++++++++---- 12 files changed, 341 insertions(+), 114 deletions(-) diff --git a/src/alias/id/esn/ESNEditor.java b/src/alias/id/esn/ESNEditor.java index 1ace4da8..2aa59876 100644 --- a/src/alias/id/esn/ESNEditor.java +++ b/src/alias/id/esn/ESNEditor.java @@ -17,25 +17,31 @@ ******************************************************************************/ package alias.id.esn; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; import alias.AliasID; import alias.ComponentEditor; +import alias.id.talkgroup.TalkgroupIDEditor; public class ESNEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "An Electronic Serial Number (ESN)" - + " is a unique radio hardware identifier normally composed of" - + " hexadecimal (0-9,A-F) characters. Use an asterisk (*) to wildcard" - + " individual digits (e.g. ABCD123* or AB**1**4)"; + private static final String HELP_TEXT = + "

Electronic Serial Number (ESN) Example

" + + "ESN: hexadecimal 0-9, A-F (e.g. ABCD1234 )
" + + "Wildcard: use an asterisk (*) to wildcard individual
" + + "digits (e.g. ABCD123* or AB**1**4)" + + ""; private JTextField mTextField; @@ -50,17 +56,27 @@ public class ESNEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "ESN:" ) ); mTextField = new JTextField(); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( ESNEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public Esn getEsn() diff --git a/src/alias/id/fleetsync/FleetsyncIDEditor.java b/src/alias/id/fleetsync/FleetsyncIDEditor.java index 9075ee7f..8bb1bc06 100644 --- a/src/alias/id/fleetsync/FleetsyncIDEditor.java +++ b/src/alias/id/fleetsync/FleetsyncIDEditor.java @@ -17,11 +17,16 @@ ******************************************************************************/ package alias.id.fleetsync; +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import javax.swing.JFormattedTextField; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; @@ -33,6 +38,7 @@ import controller.channel.Channel; import net.miginfocom.swing.MigLayout; import alias.AliasID; import alias.ComponentEditor; +import alias.id.esn.ESNEditor; public class FleetsyncIDEditor extends ComponentEditor { @@ -40,9 +46,11 @@ public class FleetsyncIDEditor extends ComponentEditor private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "A Fleetsync identifier is a" - + " composite decimal [0-9] value formatted as ggg-uuuu where" - + " g=Group and u=Unit."; + private static final String HELP_TEXT = "" + + "

Fleetsync Identifier Example

" + + "Fleetsync: ggg-uuuu where g=Group and u=Unit (e.g. 001-0001)
" + + "Wildcard: use an asterisk (*) for each digit (e.g. 001-****)" + + ""; private JTextField mTextField; @@ -57,7 +65,7 @@ public class FleetsyncIDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "Fleetsync ID:" ) ); @@ -75,13 +83,23 @@ public class FleetsyncIDEditor extends ComponentEditor mTextField = new JFormattedTextField( formatter ); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( FleetsyncIDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public FleetsyncID getFleetsyncID() diff --git a/src/alias/id/lojack/LoJackIDEditor.java b/src/alias/id/lojack/LoJackIDEditor.java index 551dc82d..6fcf9ad4 100644 --- a/src/alias/id/lojack/LoJackIDEditor.java +++ b/src/alias/id/lojack/LoJackIDEditor.java @@ -17,13 +17,17 @@ ******************************************************************************/ package alias.id.lojack; +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import javax.swing.JComboBox; import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; @@ -41,10 +45,19 @@ public class LoJackIDEditor extends ComponentEditor private JComboBox mFunctionCombo; - private static final String HELP_TEXT = "LoJack function code and five character ID." - + " The middle character in a reply ID code identifies the entity:" - + " Tower[X,Y] Transponder[0-9,A,C-H,J-N,P-W] Not Used[B,I,O,Z]." - + " Use an asterisk (*) to wildcard ID characters (e.g. AB*CD or ***12 or *****)"; + private static final String HELP_TEXT = "" + + "

LoJack Example

" + + "Function Code: 1Y-SITE ID
" + + "ID: 5 numbers or characters (e.g. 1BN47)
" + + "
" + + "Wildcard: use an asterisk (*) to wildcard ID
" + + "characters (e.g. AB*CD or ***12 or *****)

" + + "The middle character in a reply ID code identifies the
" + + "entity. Valid ID middle characters are:
" + + "
  • Tower: X,Y
  • " + + "
  • Transponder: 0-9,A,C-H,J-N,P-W
  • " + + "
  • Not Used: B,I,O,Z
  • " + + ""; public LoJackIDEditor( AliasID aliasID ) { @@ -57,7 +70,7 @@ public class LoJackIDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) ); add( new JLabel( "Function:" ) ); mFunctionCombo = new JComboBox( Function.values() ); @@ -69,6 +82,7 @@ public class LoJackIDEditor extends ComponentEditor setModified( true ); } } ); + mFunctionCombo.setToolTipText( HELP_TEXT ); add( mFunctionCombo, "growx, push" ); add( new JLabel( "ID:" ) ); @@ -87,12 +101,22 @@ public class LoJackIDEditor extends ComponentEditor mTextField = new JFormattedTextField( formatter ); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( LoJackIDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public LoJackFunctionAndID getLoJackID() diff --git a/src/alias/id/mdc/MDC1200IDEditor.java b/src/alias/id/mdc/MDC1200IDEditor.java index 2f23bacb..cae6ae6d 100644 --- a/src/alias/id/mdc/MDC1200IDEditor.java +++ b/src/alias/id/mdc/MDC1200IDEditor.java @@ -17,9 +17,14 @@ ******************************************************************************/ package alias.id.mdc; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; @@ -31,9 +36,12 @@ public class MDC1200IDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "An MDC-1200 identifier is a" - + " four digit [0-9] value. Use an asterisk (*) to wildcard" - + " individual digits (e.g. 123* or **34)"; + private static final String HELP_TEXT = "" + + "

    MDC-1200 Identifier Example

    " + + "MDC-1200: four digit [0-9] value (e.g. 1234)
    " + + "Wildcard: use an asterisk (*) to wildcard individual
    " + + "digits (e.g. 123* or **34)" + + ""; private JTextField mTextField; @@ -48,7 +56,7 @@ public class MDC1200IDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "MDC-1200 ID:" ) ); @@ -66,12 +74,22 @@ public class MDC1200IDEditor extends ComponentEditor mTextField = new JFormattedTextField( formatter ); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( MDC1200IDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public MDC1200ID getMDC1200ID() diff --git a/src/alias/id/mobileID/MINEditor.java b/src/alias/id/mobileID/MINEditor.java index aaac927d..972a6764 100644 --- a/src/alias/id/mobileID/MINEditor.java +++ b/src/alias/id/mobileID/MINEditor.java @@ -17,11 +17,14 @@ ******************************************************************************/ package alias.id.mobileID; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import javax.swing.JFormattedTextField; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; @@ -29,13 +32,18 @@ import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; import alias.AliasID; import alias.ComponentEditor; +import alias.id.esn.ESNEditor; public class MINEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "A Passport Mobile Identifier (MIN) is a" - + " six character hexadecimal value"; + private static final String HELP_TEXT = "" + + "

    Passport Mobile ID (MIN) Example

    " + + "MIN: six character hex value (e.g. AB12CD)
    " + + "Wildcard: use an asterisk (*) to wildcard individual
    " + + "digits (e.g. AB**CD or AB12**)" + + ""; private JTextField mTextField; @@ -50,7 +58,7 @@ public class MINEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "MIN:" ) ); @@ -68,12 +76,22 @@ public class MINEditor extends ComponentEditor mTextField = new JFormattedTextField( formatter ); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( MINEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public Min getMin() diff --git a/src/alias/id/mpt1327/MPT1327IDEditor.java b/src/alias/id/mpt1327/MPT1327IDEditor.java index c1e7e674..a46709b8 100644 --- a/src/alias/id/mpt1327/MPT1327IDEditor.java +++ b/src/alias/id/mpt1327/MPT1327IDEditor.java @@ -17,9 +17,14 @@ ******************************************************************************/ package alias.id.mpt1327; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; @@ -31,9 +36,13 @@ public class MPT1327IDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "An MPT-1327 identifier is a" - + " composite decimal [0-9] value formatted as ppp-iiii where" - + " p=Prefix and i=Ident."; + private static final String HELP_TEXT = "" + + "

    MPT-1327 Identifier Example

    " + + "MPT-1327: decimal (0-9) format ppp-iiii where
    " + + "p=Prefix and i=Ident (e.g. 123-0001)
    " + + "Wildcard: use an asterisk (*) to wildcard individual
    " + + "digits (e.g. ABCD123* or AB**1**4)" + + ""; private JTextField mTextField; @@ -48,7 +57,7 @@ public class MPT1327IDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "MPT-1327 ID:" ) ); @@ -66,13 +75,23 @@ public class MPT1327IDEditor extends ComponentEditor mTextField = new JFormattedTextField( formatter ); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( MPT1327IDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public MPT1327ID getMPT1327ID() diff --git a/src/alias/id/nonrecordable/NonRecordableEditor.java b/src/alias/id/nonrecordable/NonRecordableEditor.java index 220a9d5d..54aab852 100644 --- a/src/alias/id/nonrecordable/NonRecordableEditor.java +++ b/src/alias/id/nonrecordable/NonRecordableEditor.java @@ -17,7 +17,13 @@ ******************************************************************************/ package alias.id.nonrecordable; -import javax.swing.JTextArea; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; import net.miginfocom.swing.MigLayout; import alias.AliasID; @@ -27,9 +33,11 @@ public class NonRecordableEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "This identifies an alias as " - + "non-recordable. The alias can still be monitored but it will " - + "not be recorded when recording is enabled for the decoding channel."; + private static final String HELP_TEXT = "" + + "

    Non-Recordable Example

    " + + "This identifies an alias as non-recordable.

    " + + "The alias can still be monitored but it will not be recorded
    " + + "when recording is enabled for the decoding channel."; public NonRecordableEditor( AliasID aliasID ) { @@ -42,12 +50,23 @@ public class NonRecordableEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 1", "[]", "[][]" ) ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + add( new JLabel( "Non-Recordable" ), "wrap" ); + + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( NonRecordableEditor.this, + HELP_TEXT, "Examples", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } @Override diff --git a/src/alias/id/priority/PriorityEditor.java b/src/alias/id/priority/PriorityEditor.java index ccdbb36a..608b827e 100644 --- a/src/alias/id/priority/PriorityEditor.java +++ b/src/alias/id/priority/PriorityEditor.java @@ -17,10 +17,15 @@ ******************************************************************************/ package alias.id.priority; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JCheckBox; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JSlider; -import javax.swing.JTextArea; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -34,10 +39,13 @@ public class PriorityEditor extends ComponentEditor public static final String DO_NOT_MONITOR = "Do Not Monitor"; - private static final String HELP_TEXT = "Priority determines which calls" - + " have priority for playback over your computer speakers, or designates" - + " an alias for non-monitoring if you don't want to hear calls" - + " from an alias. Lower values indicate higher priority levels."; + private static final String HELP_TEXT = "" + + "

    Call Priority Example

    " + + "Priority determines which calls have priority for playback
    " + + "over your computer speakers, or designates an alias for
    " + + "no-monitoring if you don't want to hear calls from an alias.

    " + + "Lower values indicate higher priority levels." + + ""; private JCheckBox mDoNotMonitorCheckBox = new JCheckBox( DO_NOT_MONITOR ); private JSlider mPrioritySlider; @@ -54,8 +62,9 @@ public class PriorityEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); + mDoNotMonitorCheckBox.setToolTipText( HELP_TEXT ); add( mDoNotMonitorCheckBox, "span,align center" ); mPrioritySlider = new JSlider( JSlider.HORIZONTAL, @@ -76,15 +85,25 @@ public class PriorityEditor extends ComponentEditor mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); } } ); + mPrioritySlider.setToolTipText( HELP_TEXT ); mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); add( mPrioritySliderLabel ); add( mPrioritySlider, "wrap,grow" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( PriorityEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public Priority getPriority() diff --git a/src/alias/id/siteID/SiteIDEditor.java b/src/alias/id/siteID/SiteIDEditor.java index a352fd94..4850422a 100644 --- a/src/alias/id/siteID/SiteIDEditor.java +++ b/src/alias/id/siteID/SiteIDEditor.java @@ -17,8 +17,13 @@ ******************************************************************************/ package alias.id.siteID; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; @@ -29,10 +34,14 @@ public class SiteIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "Site number for P25 systems uses" - + " the format RR-SS, where RR = RF Subsystem and SS = Site Number. " - + " (e.g. RFSS 1 Site 3: 01-03). For other protocols, simply use" - + " a decimal value for the site ID"; + private static final String HELP_TEXT = "" + + "

    Site ID Example

    " + + "LTR-Net: decimal (0-9) (e.g. 019)
    " + + "MPT-1327: 5 digits (0-9) (e.g. 23619)
    " + + "Passport: 3 digits (0-9) (e.g. 019)
    " + + "P25: hex (0-9, A-F) format RR-SS where RR = RF Subsystem
    " + + "and SS = Site Number (e.g. RFSS 1 Site 1F: 01-1F)" + + ""; private JTextField mTextField; @@ -47,17 +56,28 @@ public class SiteIDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "Site ID:" ) ); mTextField = new JTextField(); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( SiteIDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + + add( example ); } public SiteID getSiteID() diff --git a/src/alias/id/status/StatusIDEditor.java b/src/alias/id/status/StatusIDEditor.java index d0575e2c..db0d6daf 100644 --- a/src/alias/id/status/StatusIDEditor.java +++ b/src/alias/id/status/StatusIDEditor.java @@ -17,9 +17,14 @@ ******************************************************************************/ package alias.id.status; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; @@ -31,10 +36,14 @@ public class StatusIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "Status numbers are used in some" - + " protocols like Fleetsync. The status number is assigned a meaning" - + " in the radio. You can assign a 3 digit status code (use leading zeros) to an" - + " alias where the alias contains the status meaning."; + private static final String HELP_TEXT = "" + + "

    Status Example

    " + + "Status numbers are used in some protocols like Fleetsync.

    " + + "The status number is assigned a meaning in the radio. You
    " + + "can assign a 3 digit status code (use leading zeros) to an
    " + + "alias where the alias contains the status meaning.

    " + + "Status: 001 engine start" + + ""; private JTextField mTextField; @@ -49,7 +58,7 @@ public class StatusIDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "Status:" ) ); @@ -67,12 +76,22 @@ public class StatusIDEditor extends ComponentEditor mTextField = new JFormattedTextField( formatter ); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( StatusIDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public StatusID getStatusID() diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index 4bae20ac..981a00c7 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -17,8 +17,13 @@ ******************************************************************************/ package alias.id.talkgroup; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; @@ -29,11 +34,14 @@ public class TalkgroupIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "Enter a formatted talkgroup identifier.\n" - + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)\n" - + "LTR: A-HH-TTT (A=Area H=Home T=Talkgroup)\n" - + "Passport: xxxxx (up to 5-digit number)\n\n" - + "Wildcard: use an asterisk (*) in place of each talkgroup digit"; + private static final String HELP_TEXT = + "

    Talkgroup Identifier Examples

    " + + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)
    " + + "LTR: A-HH-TTT where A=Area H=Home T=Talkgroup (0-01-128)
    " + + "Passport: 5-digit number (12345 or 00023)
    " + + "
    " + + "Wildcard: use an asterisk (*) in place of each talkgroup digit (e.g. 0*1*5)" + + ""; private JTextField mTextField; @@ -48,17 +56,28 @@ public class TalkgroupIDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "Talkgroup:" ) ); + mTextField = new JTextField(); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( TalkgroupIDEditor.this, + HELP_TEXT, "Examples", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public TalkgroupID getTalkgroupID() diff --git a/src/alias/id/uniqueID/UniqueIDEditor.java b/src/alias/id/uniqueID/UniqueIDEditor.java index 349f66b6..7a90c6aa 100644 --- a/src/alias/id/uniqueID/UniqueIDEditor.java +++ b/src/alias/id/uniqueID/UniqueIDEditor.java @@ -17,8 +17,13 @@ ******************************************************************************/ package alias.id.uniqueID; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + import javax.swing.JLabel; -import javax.swing.JTextArea; +import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; @@ -29,8 +34,11 @@ public class UniqueIDEditor extends ComponentEditor { private static final long serialVersionUID = 1L; - private static final String HELP_TEXT = "LTR-Net Unique ID (UID) is a system" - + " identifier assigned to each radio in the range 1 - 2097152"; + private static final String HELP_TEXT = "" + + "

    LTR-Net Unique ID (UID) Example

    " + + "UID: identifier assigned to each radio in
    " + + "the range 1 - 2097152" + + ""; private JTextField mTextField; @@ -45,17 +53,27 @@ public class UniqueIDEditor extends ComponentEditor private void initGUI() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); add( new JLabel( "Unique ID:" ) ); mTextField = new JTextField(); mTextField.getDocument().addDocumentListener( this ); + mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JTextArea helpText = new JTextArea( HELP_TEXT ); - helpText.setLineWrap( true ); - helpText.setBackground( getBackground() ); - add( helpText, "span,grow,push" ); + JLabel example = new JLabel( "Example ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( UniqueIDEditor.this, + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); } public UniqueID getUniqueID() From d1ea8656c4b139ce26ba597d584b6a380debf91d Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 29 Feb 2016 19:16:41 -0500 Subject: [PATCH 40/74] Resolved error when clicking clear on an empty message model. --- src/module/decode/event/MessageActivityModel.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/module/decode/event/MessageActivityModel.java b/src/module/decode/event/MessageActivityModel.java index ddcf17a5..25e38322 100644 --- a/src/module/decode/event/MessageActivityModel.java +++ b/src/module/decode/event/MessageActivityModel.java @@ -80,10 +80,13 @@ public class MessageActivityModel extends AbstractTableModel public void run() { int messageCount = mMessages.size(); - - mMessages.clear(); - fireTableRowsDeleted( 0, messageCount - 1 ); + if( messageCount > 0 ) + { + mMessages.clear(); + + fireTableRowsDeleted( 0, messageCount - 1 ); + } } }); } From 6d1e7a78cf2cd687e9943503226a693660f79034 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 29 Feb 2016 20:14:52 -0500 Subject: [PATCH 41/74] Work in progress - added ability to add new alias identifiers and to auto save alias/identifier changes. --- src/alias/AliasController.java | 2 +- src/alias/AliasEditor.java | 4 +- src/alias/AliasFactory.java | 34 +++++++++++ src/alias/AliasIDType.java | 36 ++++++++---- src/alias/AliasIdentifierEditor.java | 88 ++++++++++++++++++++++++++-- src/alias/AliasModel.java | 4 +- src/playlist/PlaylistManager.java | 36 +++++++++++- 7 files changed, 179 insertions(+), 25 deletions(-) diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index dbe45414..f30c0e0f 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -55,7 +55,7 @@ public class AliasController extends JPanel mAliasModel = aliasModel; mEditor = new AliasEditor( mAliasModel, settingsManager ); - mAliasModel.addAliasEventListener( mEditor ); + mAliasModel.addListener( mEditor ); mIconCellRenderer = new IconCellRenderer( settingsManager ); diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 95d43e44..03140636 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -52,9 +52,9 @@ public class AliasEditor extends JPanel implements ActionListener, Listener listener ) + public void addListener( Listener listener ) { mAliasEventBroadcaster.addListener( listener ); } - public void removeAliasEventListener( Listener listener ) + public void removeListener( Listener listener ) { mAliasEventBroadcaster.removeListener( listener ); } diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index 4fd5186f..64b21309 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -37,8 +37,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import properties.SystemProperties; +import sample.Listener; import alias.Alias; import alias.AliasDirectory; +import alias.AliasEvent; import alias.AliasList; import alias.AliasModel; import alias.Group; @@ -51,7 +53,7 @@ import controller.channel.ChannelModel; import controller.site.Site; import controller.system.SystemList; -public class PlaylistManager implements ChannelEventListener +public class PlaylistManager implements ChannelEventListener, Listener { private final static Logger mLog = LoggerFactory.getLogger( PlaylistManager.class ); @@ -83,8 +85,9 @@ public class PlaylistManager implements ChannelEventListener mAliasModel = aliasModel; mChannelModel = channelModel; - //Register for channel events so that we can save the playlist when - //any channels change + //Register for alias and channel events so that we can save the + //playlist with changes + mAliasModel.addListener( this ); mChannelModel.addListener( this ); } @@ -128,6 +131,33 @@ public class PlaylistManager implements ChannelEventListener load( playlistFolder.resolve( playlistFile ) ); } + /** + * Alias event listener method. Monitors alias events for events that + * indicate that the playlist has changed and queues automatic playlist + * saving. + */ + @Override + public void receive( AliasEvent event ) + { + if( !mPlaylistLoading ) + { + switch( event.getEvent() ) + { + case ADD: + case DELETE: + case CHANGE: + schedulePlaylistSave(); + break; + default: + //When a new event enum entry is added and received, throw an + //exception here to ensure developer adds support for the + //use case + throw new IllegalArgumentException( "Unrecognized Alias " + + "Event [" + event.getEvent().toString() + "]" ); + } + } + } + /** * Channel event listener method. Monitors channel events for events that * indicate that the playlist has changed and queues automatic playlist From 2c3dbe1b22a227b69efe485120fb337aefa3ddc2 Mon Sep 17 00:00:00 2001 From: denny Date: Tue, 1 Mar 2016 19:54:59 -0500 Subject: [PATCH 42/74] Work in progress - more updates to the Alias Identifier editor --- src/alias/AliasFactory.java | 198 ++++++++++++---------- src/alias/AliasIdentifierEditor.java | 129 +++++++++++--- src/alias/id/priority/PriorityEditor.java | 11 ++ src/alias/id/status/StatusID.java | 2 +- src/alias/id/status/StatusIDEditor.java | 2 +- 5 files changed, 225 insertions(+), 117 deletions(-) diff --git a/src/alias/AliasFactory.java b/src/alias/AliasFactory.java index 1acf5546..cc16ccbc 100644 --- a/src/alias/AliasFactory.java +++ b/src/alias/AliasFactory.java @@ -31,6 +31,102 @@ import alias.id.uniqueID.UniqueIDEditor; public class AliasFactory { + public static AliasID copyOf( AliasID id ) + { + switch( id.getType() ) + { + case ESN: + Esn originalESN = (Esn)id; + Esn copyESN = new Esn(); + copyESN.setEsn( originalESN.getEsn() ); + return copyESN; + case Fleetsync: + FleetsyncID originalFleetsyncID = (FleetsyncID)id; + FleetsyncID copyFleetsyncID = new FleetsyncID(); + copyFleetsyncID.setIdent( originalFleetsyncID.getIdent() ); + return copyFleetsyncID; + case LTRNetUID: + UniqueID originalUniqueID = (UniqueID)id; + UniqueID copyUniqueID = new UniqueID(); + copyUniqueID.setUid( originalUniqueID.getUid() ); + return copyUniqueID; + case LoJack: + LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id; + LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID(); + copyLoJackFunctionAndID.setFunction( originalLoJackFunctionAndID.getFunction() ); + copyLoJackFunctionAndID.setID( originalLoJackFunctionAndID.getID() ); + return copyLoJackFunctionAndID; + case MDC1200: + MDC1200ID originalMDC1200ID = (MDC1200ID)id; + MDC1200ID copyMDC1200ID = new MDC1200ID(); + copyMDC1200ID.setIdent( originalMDC1200ID.getIdent() ); + return copyMDC1200ID; + case MIN: + Min originalMin = (Min)id; + Min copyMin = new Min(); + copyMin.setMin( originalMin.getMin() ); + return copyMin; + case MPT1327: + MPT1327ID originalMPT1327ID = (MPT1327ID)id; + MPT1327ID copyMPT1327ID = new MPT1327ID(); + copyMPT1327ID.setIdent( originalMPT1327ID.getIdent() ); + return copyMPT1327ID; + case NonRecordable: + return new NonRecordable(); + case Priority: + Priority originalPriority = (Priority)id; + Priority copyPriority = new Priority(); + copyPriority.setPriority( originalPriority.getPriority() ); + return copyPriority; + case Site: + SiteID originalSiteID = (SiteID)id; + SiteID copySiteID = new SiteID(); + copySiteID.setSite( originalSiteID.getSite() ); + return copySiteID; + case Talkgroup: + TalkgroupID originalTalkgroupID = (TalkgroupID)id; + TalkgroupID copyTalkgroupID = new TalkgroupID(); + copyTalkgroupID.setTalkgroup( originalTalkgroupID.getTalkgroup() ); + return copyTalkgroupID; + case Status: + StatusID originalStatusID = (StatusID)id; + StatusID copyStatusID = new StatusID(); + copyStatusID.setStatus( originalStatusID.getStatus() ); + return copyStatusID; + default: + } + + return null; + } + + public static AliasAction copyOf( AliasAction action ) + { + if( action instanceof BeepAction ) + { + return new BeepAction(); + } + else if( action instanceof ClipAction ) + { + ClipAction originalClip = (ClipAction)action; + ClipAction copyClip = new ClipAction(); + copyClip.setInterval( originalClip.getInterval() ); + copyClip.setPath( originalClip.getPath() ); + copyClip.setPeriod( originalClip.getPeriod() ); + return copyClip; + } + else if( action instanceof ScriptAction ) + { + ScriptAction originalScript = (ScriptAction)action; + ScriptAction copyScript = new ScriptAction(); + copyScript.setInterval( originalScript.getInterval() ); + copyScript.setPeriod( originalScript.getPeriod() ); + copyScript.setScript( originalScript.getScript() ); + return copyScript; + } + + return null; + } + public static Alias copyOf( Alias original ) { Alias copy = new Alias( original.getName() ); @@ -41,107 +137,21 @@ public class AliasFactory for( AliasID id: original.getId() ) { - switch( id.getType() ) + AliasID copyID = copyOf( id ); + + if( copyID != null ) { - case ESN: - Esn originalESN = (Esn)id; - Esn copyESN = new Esn(); - copyESN.setEsn( originalESN.getEsn() ); - copy.addAliasID( copyESN ); - break; - case Fleetsync: - FleetsyncID originalFleetsyncID = (FleetsyncID)id; - FleetsyncID copyFleetsyncID = new FleetsyncID(); - copyFleetsyncID.setIdent( originalFleetsyncID.getIdent() ); - copy.addAliasID( copyFleetsyncID ); - break; - case LTRNetUID: - UniqueID originalUniqueID = (UniqueID)id; - UniqueID copyUniqueID = new UniqueID(); - copyUniqueID.setUid( originalUniqueID.getUid() ); - copy.addAliasID( copyUniqueID ); - break; - case LoJack: - LoJackFunctionAndID originalLoJackFunctionAndID = (LoJackFunctionAndID)id; - LoJackFunctionAndID copyLoJackFunctionAndID = new LoJackFunctionAndID(); - copyLoJackFunctionAndID.setFunction( originalLoJackFunctionAndID.getFunction() ); - copyLoJackFunctionAndID.setID( originalLoJackFunctionAndID.getID() ); - copy.addAliasID( copyLoJackFunctionAndID ); - break; - case MDC1200: - MDC1200ID originalMDC1200ID = (MDC1200ID)id; - MDC1200ID copyMDC1200ID = new MDC1200ID(); - copyMDC1200ID.setIdent( originalMDC1200ID.getIdent() ); - copy.addAliasID( copyMDC1200ID ); - break; - case MIN: - Min originalMin = (Min)id; - Min copyMin = new Min(); - copyMin.setMin( originalMin.getMin() ); - copy.addAliasID( copyMin ); - break; - case MPT1327: - MPT1327ID originalMPT1327ID = (MPT1327ID)id; - MPT1327ID copyMPT1327ID = new MPT1327ID(); - copyMPT1327ID.setIdent( originalMPT1327ID.getIdent() ); - copy.addAliasID( copyMPT1327ID ); - break; - case NonRecordable: - copy.addAliasID( new NonRecordable() ); - break; - case Priority: - Priority originalPriority = (Priority)id; - Priority copyPriority = new Priority(); - copyPriority.setPriority( originalPriority.getPriority() ); - copy.addAliasID( copyPriority ); - break; - case Site: - SiteID originalSiteID = (SiteID)id; - SiteID copySiteID = new SiteID(); - copySiteID.setSite( originalSiteID.getSite() ); - copy.addAliasID( copySiteID ); - break; - case Talkgroup: - TalkgroupID originalTalkgroupID = (TalkgroupID)id; - TalkgroupID copyTalkgroupID = new TalkgroupID(); - copyTalkgroupID.setTalkgroup( originalTalkgroupID.getTalkgroup() ); - copy.addAliasID( copyTalkgroupID ); - break; - case Status: - default: - throw new IllegalArgumentException( "Unrecognized alias " - + "ID type: " + id.getType() ); + copy.addAliasID( copyID ); } } for( AliasAction action: original.getAction() ) { - if( action instanceof BeepAction ) + AliasAction copyAction = copyOf( action ); + + if( copyAction != null ) { - copy.addAliasAction( new BeepAction() ); - } - else if( action instanceof ClipAction ) - { - ClipAction originalClip = (ClipAction)action; - ClipAction copyClip = new ClipAction(); - copyClip.setInterval( originalClip.getInterval() ); - copyClip.setPath( originalClip.getPath() ); - copyClip.setPeriod( originalClip.getPeriod() ); - copy.addAliasAction( copyClip ); - } - else if( action instanceof ScriptAction ) - { - ScriptAction originalScript = (ScriptAction)action; - ScriptAction copyScript = new ScriptAction(); - copyScript.setInterval( originalScript.getInterval() ); - copyScript.setPeriod( originalScript.getPeriod() ); - copyScript.setScript( originalScript.getScript() ); - copy.addAliasAction( copyScript ); - } - else - { - throw new IllegalArgumentException( "Unrecognized alias " - + "action: " + action.getClass() ); + copy.addAliasAction( copyAction ); } } diff --git a/src/alias/AliasIdentifierEditor.java b/src/alias/AliasIdentifierEditor.java index ca609412..3a8fca14 100644 --- a/src/alias/AliasIdentifierEditor.java +++ b/src/alias/AliasIdentifierEditor.java @@ -27,7 +27,6 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; import alias.AliasEvent.Event; public class AliasIdentifierEditor extends AliasConfigurationEditor @@ -38,6 +37,10 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor private static ListModel EMPTY_MODEL = new DefaultListModel<>(); private JList mAliasIDList = new JList<>( EMPTY_MODEL ); + private JButton mNewIDButton; + private JButton mCloneIDButton; + private JButton mDeleteIDButton; + private EditorContainer mEditorContainer = new EditorContainer(); private Alias mAlias; @@ -73,6 +76,14 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor AliasID selected = (AliasID)selectedItem; mEditorContainer.setAliasID( selected ); + + mCloneIDButton.setEnabled( true ); + mDeleteIDButton.setEnabled( true ); + } + else + { + mCloneIDButton.setEnabled( false ); + mDeleteIDButton.setEnabled( false ); } } } @@ -83,9 +94,9 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor add( mEditorContainer, "span,grow" ); - final JButton newButton = new JButton( "New ..." ); + mNewIDButton = new JButton( "New ..." ); - newButton.addMouseListener( new MouseAdapter() + mNewIDButton.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) @@ -111,9 +122,57 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor } } ); - add( newButton ); - add( new JButton( "Copy" ) ); - add( new JButton( "Delete" ), "wrap" ); + add( mNewIDButton ); + + mCloneIDButton = new JButton( "Clone" ); + mCloneIDButton.setEnabled( false ); + mCloneIDButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + AliasID selected = mAliasIDList.getSelectedValue(); + + if( selected != null ) + { + AliasID clone = AliasFactory.copyOf( selected ); + + if( clone != null ) + { + addAliasIDToList( clone ); + } + } + } + } ); + add( mCloneIDButton ); + + mDeleteIDButton = new JButton( "Delete" ); + mDeleteIDButton.setEnabled( false ); + mDeleteIDButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasID selected = mAliasIDList.getSelectedValue(); + + if( selected != null ) + { + int choice = JOptionPane.showConfirmDialog( mDeleteIDButton, + "Do you want to delete [" + selected.toString() + "]", + "Are you sure?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + mAlias.removeAliasID( selected ); + + mAliasModel.broadcast( new AliasEvent( mAlias, Event.CHANGE ) ); + + setAlias( mAlias ); + } + } + } + } ); + add( mDeleteIDButton, "wrap" ); } @Override @@ -154,6 +213,11 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor @Override public void save() { + if( mEditorContainer.isModified() ) + { + mEditorContainer.save(); + } + mAliasModel.broadcast( new AliasEvent( mAlias, Event.CHANGE ) ); } @@ -170,6 +234,24 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor add( mEditor ); } + public boolean isModified() + { + if( mEditor != null ) + { + return mEditor.isModified(); + } + + return false; + } + + public void save() + { + if( mEditor != null ) + { + mEditor.save(); + } + } + public void setAliasID( AliasID aliasID ) { if( mEditor != null ) @@ -199,6 +281,25 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor } } + private void addAliasIDToList( final AliasID id ) + { + if( id != null && mAlias != null ) + { + mAlias.addAliasID( id ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + setAlias( mAlias ); + + mAliasIDList.setSelectedValue( id, true ); + } + } ); + } + } + public class AddAliasIdentifierItem extends JMenuItem { private static final long serialVersionUID = 1L; @@ -218,21 +319,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor { final AliasID id = AliasFactory.getAliasID( mAliasIDType ); - if( id != null && mAlias != null ) - { - mAlias.addAliasID( id ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - setAlias( mAlias ); - - mAliasIDList.setSelectedValue( id, true ); - } - } ); - } + addAliasIDToList( id ); } } ); } diff --git a/src/alias/id/priority/PriorityEditor.java b/src/alias/id/priority/PriorityEditor.java index 608b827e..1190d2ec 100644 --- a/src/alias/id/priority/PriorityEditor.java +++ b/src/alias/id/priority/PriorityEditor.java @@ -19,6 +19,8 @@ package alias.id.priority; import java.awt.Color; import java.awt.Cursor; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -65,6 +67,14 @@ public class PriorityEditor extends ComponentEditor setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); mDoNotMonitorCheckBox.setToolTipText( HELP_TEXT ); + mDoNotMonitorCheckBox.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); add( mDoNotMonitorCheckBox, "span,align center" ); mPrioritySlider = new JSlider( JSlider.HORIZONTAL, @@ -83,6 +93,7 @@ public class PriorityEditor extends ComponentEditor public void stateChanged( ChangeEvent e ) { mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); + setModified( true ); } } ); mPrioritySlider.setToolTipText( HELP_TEXT ); diff --git a/src/alias/id/status/StatusID.java b/src/alias/id/status/StatusID.java index 40ae82b8..5ce9c2ab 100644 --- a/src/alias/id/status/StatusID.java +++ b/src/alias/id/status/StatusID.java @@ -43,7 +43,7 @@ public class StatusID extends AliasID public String toString() { - return "Status: " + mStatus; + return "Status: " + String.format( "%03d", mStatus ); } @Override diff --git a/src/alias/id/status/StatusIDEditor.java b/src/alias/id/status/StatusIDEditor.java index db0d6daf..6b29e174 100644 --- a/src/alias/id/status/StatusIDEditor.java +++ b/src/alias/id/status/StatusIDEditor.java @@ -113,7 +113,7 @@ public class StatusIDEditor extends ComponentEditor if( statusID != null ) { - mTextField.setText( String.valueOf( statusID.getStatus() ) ); + mTextField.setText( String.format( "%03d", statusID.getStatus() ) ); } setModified( false ); From 00d2b43c2ac869ea29b2de9e8d6de6434c8aa630 Mon Sep 17 00:00:00 2001 From: denny Date: Fri, 4 Mar 2016 09:33:46 -0500 Subject: [PATCH 43/74] wip: finished alias, alias ID and alias action editing framework. --- src/alias/Alias.java | 2 + src/alias/AliasController.java | 9 +- src/alias/AliasEditor.java | 108 ++++-- src/alias/AliasFactory.java | 74 +++- src/alias/AliasList.java | 1 + src/alias/AliasModel.java | 2 +- src/alias/AliasNameEditor.java | 96 ++++- src/alias/AliasNode.java | 1 + src/alias/ComponentEditor.java | 16 - src/alias/EmptyAliasActionEditor.java | 32 -- src/alias/EmptyAliasIDEditor.java | 31 -- src/alias/action/AliasAction.java | 4 + src/alias/action/AliasActionEditor.java | 349 ++++++++++++++++++ .../{ => action}/AliasActionListener.java | 4 +- .../AliasActionType.java} | 33 +- src/alias/action/beep/BeepAction.java | 14 + src/alias/action/beep/BeepActionEditor.java | 190 ++++++---- src/alias/action/beep/BeepActionNode.java | 2 +- src/alias/action/clip/ClipAction.java | 22 +- src/alias/action/clip/ClipActionEditor.java | 282 +++++++++----- src/alias/action/clip/ClipActionNode.java | 2 +- src/alias/action/script/ScriptAction.java | 20 + .../action/script/ScriptActionEditor.java | 259 ++++++++----- src/alias/action/script/ScriptActionNode.java | 2 +- src/alias/{ => id}/AliasID.java | 3 +- src/alias/{ => id}/AliasIDNode.java | 2 +- src/alias/{ => id}/AliasIDType.java | 13 +- src/alias/{ => id}/AliasIdentifierEditor.java | 73 ++-- src/alias/id/esn/ESNEditor.java | 21 +- src/alias/id/esn/ESNNode.java | 2 +- src/alias/id/esn/Esn.java | 4 +- src/alias/id/fleetsync/FleetsyncID.java | 4 +- src/alias/id/fleetsync/FleetsyncIDEditor.java | 27 +- src/alias/id/fleetsync/FleetsyncIDNode.java | 2 +- src/alias/id/lojack/LoJackFunctionAndID.java | 4 +- src/alias/id/lojack/LoJackIDEditor.java | 19 +- src/alias/id/lojack/LoJackIDNode.java | 2 +- src/alias/id/mdc/MDC1200ID.java | 4 +- src/alias/id/mdc/MDC1200IDEditor.java | 19 +- src/alias/id/mdc/MDC1200IDNode.java | 2 +- src/alias/id/mobileID/MINEditor.java | 21 +- src/alias/id/mobileID/MINNode.java | 2 +- src/alias/id/mobileID/Min.java | 4 +- src/alias/id/mpt1327/MPT1327ID.java | 4 +- src/alias/id/mpt1327/MPT1327IDEditor.java | 19 +- src/alias/id/mpt1327/MPT1327IDNode.java | 2 +- src/alias/id/nonrecordable/NonRecordable.java | 6 +- .../id/nonrecordable/NonRecordableEditor.java | 22 +- .../id/nonrecordable/NonRecordableNode.java | 4 +- src/alias/id/priority/Priority.java | 6 +- src/alias/id/priority/PriorityEditor.java | 21 +- src/alias/id/priority/PriorityNode.java | 2 +- src/alias/id/siteID/SiteID.java | 4 +- src/alias/id/siteID/SiteIDEditor.java | 19 +- src/alias/id/siteID/SiteIDNode.java | 2 +- src/alias/id/status/StatusID.java | 4 +- src/alias/id/status/StatusIDEditor.java | 19 +- src/alias/id/status/StatusIDNode.java | 2 +- src/alias/id/talkgroup/TalkgroupID.java | 4 +- src/alias/id/talkgroup/TalkgroupIDEditor.java | 19 +- src/alias/id/talkgroup/TalkgroupIDNode.java | 2 +- src/alias/id/uniqueID/UniqueID.java | 4 +- src/alias/id/uniqueID/UniqueIDEditor.java | 19 +- src/alias/id/uniqueID/UniqueIDNode.java | 2 +- src/gui/editor/DocumentListenerEditor.java | 29 ++ src/gui/editor/Editor.java | 94 +++++ src/gui/editor/EmptyEditor.java | 26 ++ src/module/ProcessingChain.java | 1 - 68 files changed, 1472 insertions(+), 647 deletions(-) delete mode 100644 src/alias/EmptyAliasActionEditor.java delete mode 100644 src/alias/EmptyAliasIDEditor.java create mode 100644 src/alias/action/AliasActionEditor.java rename src/alias/{ => action}/AliasActionListener.java (96%) rename src/alias/{AliasConfigurationEditor.java => action/AliasActionType.java} (69%) rename src/alias/{ => id}/AliasID.java (98%) rename src/alias/{ => id}/AliasIDNode.java (98%) rename src/alias/{ => id}/AliasIDType.java (86%) rename src/alias/{ => id}/AliasIdentifierEditor.java (76%) create mode 100644 src/gui/editor/DocumentListenerEditor.java create mode 100644 src/gui/editor/Editor.java create mode 100644 src/gui/editor/EmptyEditor.java diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 1f29d060..46aef07c 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -27,6 +27,8 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import alias.action.AliasAction; +import alias.id.AliasID; +import alias.id.AliasIDType; import alias.id.priority.Priority; @XmlSeeAlso( { AliasID.class, AliasAction.class } ) diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index f30c0e0f..77fa19c9 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -55,11 +55,12 @@ public class AliasController extends JPanel mAliasModel = aliasModel; mEditor = new AliasEditor( mAliasModel, settingsManager ); - mAliasModel.addListener( mEditor ); mIconCellRenderer = new IconCellRenderer( settingsManager ); init(); + + mAliasModel.addListener( mEditor ); } private void init() @@ -124,7 +125,7 @@ public class AliasController extends JPanel @Override public void valueChanged( ListSelectionEvent event ) { - //This limits event firing to only when selection is complete + //Limits event firing to only when selection is complete if( !event.getValueIsAdjusting() ) { int selectedRow = mAliasTable.getSelectedRow(); @@ -137,14 +138,14 @@ public class AliasController extends JPanel if( alias != null ) { - mEditor.setAlias( alias ); + mEditor.setItem( alias ); mCopyButton.setEnabled( true ); mDeleteButton.setEnabled( true ); } } else { - mEditor.setAlias( null ); + mEditor.setItem( null ); mCopyButton.setEnabled( false ); mDeleteButton.setEnabled( false ); } diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 03140636..75c176e4 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -17,25 +17,34 @@ ******************************************************************************/ package alias; +import gui.editor.Editor; + import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JPanel; import javax.swing.JSeparator; import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import sample.Listener; import settings.SettingsManager; import alias.AliasEvent.Event; +import alias.action.AliasActionEditor; +import alias.id.AliasIdentifierEditor; import com.jidesoft.swing.JideTabbedPane; -public class AliasEditor extends JPanel implements ActionListener, Listener +public class AliasEditor extends Editor implements ActionListener, Listener { private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( AliasEditor.class ); private static final String DEFAULT_NAME = "select an alias"; @@ -44,41 +53,44 @@ public class AliasEditor extends JPanel implements ActionListener, Listener + ******************************************************************************/ package alias; +import gui.editor.Editor; +import gui.editor.EmptyEditor; import alias.action.AliasAction; +import alias.action.AliasActionType; import alias.action.beep.BeepAction; +import alias.action.beep.BeepActionEditor; import alias.action.clip.ClipAction; +import alias.action.clip.ClipActionEditor; import alias.action.script.ScriptAction; +import alias.action.script.ScriptActionEditor; +import alias.id.AliasID; +import alias.id.AliasIDType; import alias.id.esn.ESNEditor; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; import alias.id.fleetsync.FleetsyncIDEditor; -import alias.id.lojack.LoJackIDEditor; import alias.id.lojack.LoJackFunctionAndID; -import alias.id.mdc.MDC1200IDEditor; +import alias.id.lojack.LoJackIDEditor; import alias.id.mdc.MDC1200ID; +import alias.id.mdc.MDC1200IDEditor; import alias.id.mobileID.MINEditor; import alias.id.mobileID.Min; import alias.id.mpt1327.MPT1327ID; @@ -20,8 +45,8 @@ import alias.id.nonrecordable.NonRecordable; import alias.id.nonrecordable.NonRecordableEditor; import alias.id.priority.Priority; import alias.id.priority.PriorityEditor; -import alias.id.siteID.SiteIDEditor; import alias.id.siteID.SiteID; +import alias.id.siteID.SiteIDEditor; import alias.id.status.StatusID; import alias.id.status.StatusIDEditor; import alias.id.talkgroup.TalkgroupID; @@ -158,7 +183,7 @@ public class AliasFactory return copy; } - public static ComponentEditor getEditor( AliasID aliasID ) + public static Editor getEditor( AliasID aliasID ) { if( aliasID != null ) { @@ -193,28 +218,27 @@ public class AliasFactory } } - return new EmptyAliasIDEditor(); + return new EmptyEditor(); } - public static ComponentEditor getEditor( AliasAction aliasAction ) + public static Editor getEditor( AliasAction aliasAction ) { if( aliasAction != null ) { - if( aliasAction instanceof BeepAction ) + switch( aliasAction.getType() ) { - - } - else if( aliasAction instanceof ClipAction ) - { - - } - else if( aliasAction instanceof ScriptAction ) - { - + case BEEP: + return new BeepActionEditor( aliasAction ); + case CLIP: + return new ClipActionEditor( aliasAction ); + case SCRIPT: + return new ScriptActionEditor( aliasAction ); + default: + break; } } - return new EmptyAliasActionEditor(); + return new EmptyEditor(); } public static AliasID getAliasID( AliasIDType type ) @@ -249,4 +273,20 @@ public class AliasFactory throw new IllegalArgumentException( "Unrecognized Alias ID type: " + type ); } } + + public static AliasAction getAliasAction( AliasActionType type ) + { + switch( type ) + { + case BEEP: + return new BeepAction(); + case CLIP: + return new ClipAction(); + case SCRIPT: + return new ScriptAction(); + default: + throw new IllegalArgumentException( "Unrecognized Alias Action type: " + type ); + } + } + } diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index 52eb6efe..e62ec72f 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -34,6 +34,7 @@ import alias.action.AliasAction; import alias.action.beep.BeepAction; import alias.action.clip.ClipAction; import alias.action.script.ScriptAction; +import alias.id.AliasID; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; import alias.id.lojack.LoJackFunctionAndID; diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index 9542c9f1..f248ded6 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -48,7 +48,7 @@ public class AliasModel extends AbstractTableModel public static final int COLUMN_ICON = 3; public static final int COLUMN_COLOR = 4; - private List mAliases = new ArrayList<>(); + private List mAliases = new CopyOnWriteArrayList<>(); private Broadcaster mAliasEventBroadcaster = new Broadcaster<>(); public AliasModel() diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index 8455f836..3a99e098 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -1,5 +1,7 @@ package alias; +import gui.editor.Editor; + import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; @@ -14,17 +16,24 @@ import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.AliasEvent.Event; import map.IconManager; import map.MapIcon; import map.MapIconListCellRenderer; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; -public class AliasNameEditor extends AliasConfigurationEditor +public class AliasNameEditor extends Editor { private static final long serialVersionUID = 1L; - + private final static Logger mLog = LoggerFactory.getLogger( AliasNameEditor.class ); + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); private JComboBox mListCombo = new JComboBox<>( EMPTY_MODEL ); @@ -34,7 +43,6 @@ public class AliasNameEditor extends AliasConfigurationEditor private JButton mButtonColor; private JButton mBtnIconManager; - private Alias mAlias; private AliasModel mAliasModel; private SettingsManager mSettingsManager; @@ -52,6 +60,23 @@ public class AliasNameEditor extends AliasConfigurationEditor add( new JLabel( "Name:" ) ); mName = new JTextField(); + mName.getDocument().addDocumentListener( new DocumentListener() + { + @Override + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ){} + } ); add( mName, "wrap" ); add( new JLabel( "List:" ) ); @@ -76,12 +101,22 @@ public class AliasNameEditor extends AliasConfigurationEditor groups.toArray( new String[ groups.size() ] ) ) );; } } + + setModified( true ); } } ); add( mListCombo, "wrap" ); add( new JLabel( "Group:" ) ); mGroupCombo.setEditable( true ); + mGroupCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); add( mGroupCombo, "wrap" ); add( new JLabel( "Color:" ) ); @@ -95,12 +130,14 @@ public class AliasNameEditor extends AliasConfigurationEditor Color newColor = JColorChooser.showDialog( AliasNameEditor.this, "Choose color for this alias", - ( mAlias != null ? mAlias.getMapColor() : null ) ); + ( hasItem() ? getItem().getMapColor() : null ) ); if( newColor != null ) { mButtonColor.setForeground( newColor ); mButtonColor.setBackground( newColor ); + + setModified( true ); } } } ); @@ -113,6 +150,14 @@ public class AliasNameEditor extends AliasConfigurationEditor MapIconListCellRenderer renderer = new MapIconListCellRenderer(); renderer.setPreferredSize( new Dimension( 200, 30 ) ); mMapIconCombo.setRenderer( renderer ); + mMapIconCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); add( mMapIconCombo, "wrap" ); @@ -138,16 +183,18 @@ public class AliasNameEditor extends AliasConfigurationEditor } ); } } ); - add( mBtnIconManager, "span 2,wrap" ); + add( mBtnIconManager, "span 2,wrap" ); + + setModified( false ); } @Override - public void setAlias( Alias alias ) + public void setItem( Alias alias ) { - mAlias = alias; - - if( mAlias != null ) + super.setItem( alias ); + + if( hasItem() ) { mName.setText( alias.getName() ); @@ -163,9 +210,9 @@ public class AliasNameEditor extends AliasConfigurationEditor listNames.toArray( new String[ listNames.size() ] ) ) );; } - mListCombo.setSelectedItem( mAlias.getList() ); + mListCombo.setSelectedItem( alias.getList() ); - List groupNames = mAliasModel.getGroupNames( mAlias.getList() ); + List groupNames = mAliasModel.getGroupNames( alias.getList() ); if( groupNames.isEmpty() ) { @@ -177,14 +224,14 @@ public class AliasNameEditor extends AliasConfigurationEditor groupNames.toArray( new String[ groupNames.size() ] ) ) );; } - mGroupCombo.setSelectedItem( mAlias.getGroup() ); + mGroupCombo.setSelectedItem( alias.getGroup() ); - Color color = mAlias.getMapColor(); + Color color = alias.getMapColor(); mButtonColor.setBackground( color ); mButtonColor.setForeground( color ); - String iconName = mAlias.getIconName(); + String iconName = alias.getIconName(); if( iconName == null ) { @@ -207,31 +254,40 @@ public class AliasNameEditor extends AliasConfigurationEditor mButtonColor.setBackground( getBackground() ); mButtonColor.setForeground( getForeground() ); } + + setModified( false ); } @Override public void save() { - if( mAlias != null ) + if( hasItem() && isModified() ) { + Alias alias = getItem(); + if( mListCombo.getSelectedItem() != null ) { - mAlias.setList( (String)mListCombo.getSelectedItem() ); + alias.setList( (String)mListCombo.getSelectedItem() ); } if( mGroupCombo.getSelectedItem() != null ) { - mAlias.setGroup( (String)mGroupCombo.getSelectedItem() ); + alias.setGroup( (String)mGroupCombo.getSelectedItem() ); } - mAlias.setName( mName.getText() ); + alias.setName( mName.getText() ); - mAlias.setColor( mButtonColor.getBackground().getRGB() ); + alias.setColor( mButtonColor.getBackground().getRGB() ); if( mMapIconCombo.getSelectedItem() != null ) { - mAlias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); + alias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); } + + //Broadcast an alias change event to save the updates + mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); } + + setModified( false ); } } diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java index ca120982..634c5665 100644 --- a/src/alias/AliasNode.java +++ b/src/alias/AliasNode.java @@ -36,6 +36,7 @@ import alias.action.clip.ClipAction; import alias.action.clip.ClipActionNode; import alias.action.script.ScriptAction; import alias.action.script.ScriptActionNode; +import alias.id.AliasID; import alias.id.esn.ESNNode; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; diff --git a/src/alias/ComponentEditor.java b/src/alias/ComponentEditor.java index 3de3d30c..9cc909bf 100644 --- a/src/alias/ComponentEditor.java +++ b/src/alias/ComponentEditor.java @@ -18,7 +18,6 @@ package alias; import javax.swing.JPanel; -import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; public abstract class ComponentEditor extends JPanel implements DocumentListener @@ -71,21 +70,6 @@ public abstract class ComponentEditor extends JPanel implements DocumentListe return mModified; } - @Override - public void insertUpdate( DocumentEvent e ) - { - setModified( true ); - } - - @Override - public void removeUpdate( DocumentEvent e ) - { - setModified( true ); - } - - @Override - public void changedUpdate( DocumentEvent e ) { } - public void setComponentModificationListener( ComponentModificationListener listener ) { mListener = listener; diff --git a/src/alias/EmptyAliasActionEditor.java b/src/alias/EmptyAliasActionEditor.java deleted file mode 100644 index d00dfaa7..00000000 --- a/src/alias/EmptyAliasActionEditor.java +++ /dev/null @@ -1,32 +0,0 @@ -package alias; - -import javax.swing.JLabel; - -import net.miginfocom.swing.MigLayout; -import alias.action.AliasAction; - -public class EmptyAliasActionEditor extends ComponentEditor -{ - private static final long serialVersionUID = 1L; - - public EmptyAliasActionEditor() - { - setLayout( new MigLayout( "", "[center]", "[][grow]" ) ); - add( new JLabel( "Please select an alias action" ), "wrap" ); - } - - @Override - public void setComponent( AliasAction t ) - { - } - - @Override - public void save() - { - } - - @Override - public void reset() - { - } -} diff --git a/src/alias/EmptyAliasIDEditor.java b/src/alias/EmptyAliasIDEditor.java deleted file mode 100644 index 3c7908f6..00000000 --- a/src/alias/EmptyAliasIDEditor.java +++ /dev/null @@ -1,31 +0,0 @@ -package alias; - -import javax.swing.JLabel; - -import net.miginfocom.swing.MigLayout; - -public class EmptyAliasIDEditor extends ComponentEditor -{ - private static final long serialVersionUID = 1L; - - public EmptyAliasIDEditor() - { - setLayout( new MigLayout( "", "[center]", "[][grow]" ) ); - add( new JLabel( "Please select an identifier" ), "wrap" ); - } - - @Override - public void setComponent( AliasID t ) - { - } - - @Override - public void save() - { - } - - @Override - public void reset() - { - } -} diff --git a/src/alias/action/AliasAction.java b/src/alias/action/AliasAction.java index b2d409b2..4d10dd29 100644 --- a/src/alias/action/AliasAction.java +++ b/src/alias/action/AliasAction.java @@ -19,6 +19,7 @@ package alias.action; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; import message.Message; import alias.Alias; @@ -37,6 +38,9 @@ public abstract class AliasAction public AliasAction() { } + + @XmlTransient + public abstract AliasActionType getType(); /** * Task to execute when an alias action is defined. The message argument is diff --git a/src/alias/action/AliasActionEditor.java b/src/alias/action/AliasActionEditor.java new file mode 100644 index 00000000..96b0f9a9 --- /dev/null +++ b/src/alias/action/AliasActionEditor.java @@ -0,0 +1,349 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias.action; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JList; +import javax.swing.JMenuItem; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; +import javax.swing.ListModel; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.AliasEvent; +import alias.AliasEvent.Event; +import alias.AliasFactory; +import alias.AliasModel; + +public class AliasActionEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( AliasActionEditor.class ); + + private static ListModel EMPTY_MODEL = new DefaultListModel<>(); + private JList mAliasActionList = new JList<>( EMPTY_MODEL ); + private JButton mNewActionButton; + private JButton mCloneActionButton; + private JButton mDeleteActionButton; + private EditorContainer mEditorContainer = new EditorContainer(); + + private AliasModel mAliasModel; + + public AliasActionEditor( AliasModel model ) + { + mAliasModel = model; + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); + + mAliasActionList.setVisibleRowCount( 6 ); + mAliasActionList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mAliasActionList.setLayoutOrientation( JList.VERTICAL ); + mAliasActionList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + JList list = (JList)event.getSource(); + + Object selectedItem = list.getSelectedValue(); + + if( selectedItem != null && selectedItem instanceof AliasAction ) + { + AliasAction selected = (AliasAction)selectedItem; + + mEditorContainer.setAliasAction( selected ); + + mCloneActionButton.setEnabled( true ); + mDeleteActionButton.setEnabled( true ); + } + else + { + mCloneActionButton.setEnabled( false ); + mDeleteActionButton.setEnabled( false ); + } + } + } + } ); + + JScrollPane scroller = new JScrollPane( mAliasActionList ); + add( scroller, "span,grow" ); + + add( mEditorContainer, "span,grow" ); + + mNewActionButton = new JButton( "New ..." ); + mNewActionButton.setToolTipText( "Create a new Alias Action" ); + mNewActionButton.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JPopupMenu menu = new JPopupMenu(); + + menu.add( new AddAliasActionItem( AliasActionType.BEEP ) ); + menu.add( new AddAliasActionItem( AliasActionType.CLIP ) ); + menu.add( new AddAliasActionItem( AliasActionType.SCRIPT ) ); + + menu.show( e.getComponent(), e.getX(), e.getY() ); + } + } ); + + add( mNewActionButton ); + + mCloneActionButton = new JButton( "Clone" ); + mCloneActionButton.setToolTipText( "Create a copy of the currently selected action" ); + mCloneActionButton.setEnabled( false ); + mCloneActionButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + AliasAction selected = mAliasActionList.getSelectedValue(); + + if( selected != null ) + { + AliasAction clone = AliasFactory.copyOf( selected ); + + if( clone != null ) + { + addAliasActionToList( clone ); + } + } + } + } ); + add( mCloneActionButton ); + + mDeleteActionButton = new JButton( "Delete" ); + mDeleteActionButton.setToolTipText( "Delete the currently selected action" ); + mDeleteActionButton.setEnabled( false ); + mDeleteActionButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasAction selected = mAliasActionList.getSelectedValue(); + + if( selected != null ) + { + int choice = JOptionPane.showConfirmDialog( mDeleteActionButton, + "Do you want to delete [" + selected.toString() + "]", + "Are you sure?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION && hasItem() ) + { + Alias alias = getItem(); + + alias.removeAliasAction( selected ); + + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); + + setItem( alias ); + } + } + } + } ); + add( mDeleteActionButton, "wrap" ); + } + + @Override + public void setItem( Alias alias ) + { + super.setItem( alias ); + + if( alias == null || alias.getAction().isEmpty() ) + { + mAliasActionList.setModel( EMPTY_MODEL ); + } + else + { + DefaultListModel model = new DefaultListModel<>(); + + List actions = alias.getAction(); + + Collections.sort( actions, new Comparator() + { + @Override + public int compare( AliasAction o1, AliasAction o2 ) + { + return o1.toString().compareTo( o2.toString() ); + } + } ); + + for( AliasAction action: actions ) + { + model.addElement( action ); + } + + mAliasActionList.setModel( model ); + } + + mEditorContainer.setAliasAction( null ); + } + + @Override + public void save() + { + if( mEditorContainer.isModified() ) + { + mEditorContainer.save(); + + if( hasItem() ) + { + mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); + } + } + } + + public class EditorContainer extends JPanel + { + private static final long serialVersionUID = 1L; + + private Editor mEditor = new EmptyEditor<>(); + + public EditorContainer() + { + setLayout( new MigLayout( "","[grow,fill]", "[grow,fill]" ) ); + + add( mEditor ); + } + + public boolean isModified() + { + if( mEditor != null ) + { + return mEditor.isModified(); + } + + return false; + } + + public void save() + { + if( mEditor != null ) + { + mEditor.save(); + } + } + + public void setAliasAction( AliasAction aliasAction ) + { + if( mEditor != null ) + { + if( mEditor.isModified() ) + { + int option = JOptionPane.showConfirmDialog( + EditorContainer.this, + "Action settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + mEditor.save(); + } + } + } + removeAll(); + + //This will always give us an editor + mEditor = AliasFactory.getEditor( aliasAction ); + + add( mEditor ); + + revalidate(); + } + } + + private void addAliasActionToList( final AliasAction action ) + { + if( action != null && hasItem() ) + { + final Alias alias = getItem(); + + alias.addAliasAction( action ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + setItem( alias ); + + mAliasActionList.setSelectedValue( action, true ); + } + } ); + } + } + + public class AddAliasActionItem extends JMenuItem + { + private static final long serialVersionUID = 1L; + + private AliasActionType mAliasActionType; + + public AddAliasActionItem( AliasActionType type ) + { + super( type.toString() ); + + mAliasActionType = type; + + addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final AliasAction action = + AliasFactory.getAliasAction( mAliasActionType ); + + addAliasActionToList( action ); + } + } ); + } + } +} diff --git a/src/alias/AliasActionListener.java b/src/alias/action/AliasActionListener.java similarity index 96% rename from src/alias/AliasActionListener.java rename to src/alias/action/AliasActionListener.java index 5c97948c..a967e446 100644 --- a/src/alias/AliasActionListener.java +++ b/src/alias/action/AliasActionListener.java @@ -15,7 +15,9 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package alias.action; + +import alias.AliasEvent; /** * Interface for receiving alias events. Each listener will receive diff --git a/src/alias/AliasConfigurationEditor.java b/src/alias/action/AliasActionType.java similarity index 69% rename from src/alias/AliasConfigurationEditor.java rename to src/alias/action/AliasActionType.java index 1add4761..b54ddb06 100644 --- a/src/alias/AliasConfigurationEditor.java +++ b/src/alias/action/AliasActionType.java @@ -1,5 +1,4 @@ /******************************************************************************* - * SDR Trunk * Copyright (C) 2014-2016 Dennis Sheirer * @@ -16,21 +15,23 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package alias.action; -import javax.swing.JPanel; - -public abstract class AliasConfigurationEditor extends JPanel +public enum AliasActionType { - private static final long serialVersionUID = 1L; - - /** - * Sets or resets the configuration editor components - */ - public abstract void setAlias( Alias alias ); - - /** - * Saves the contents of the editor components to the configuration - */ - public abstract void save(); + BEEP( "Beep" ), + CLIP( "Play Clip" ), + SCRIPT( "Run Script" ); + + private String mLabel; + + private AliasActionType( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } } diff --git a/src/alias/action/beep/BeepAction.java b/src/alias/action/beep/BeepAction.java index b260adbb..65ebd2bd 100644 --- a/src/alias/action/beep/BeepAction.java +++ b/src/alias/action/beep/BeepAction.java @@ -2,13 +2,27 @@ package alias.action.beep; import message.Message; import alias.Alias; +import alias.action.AliasActionType; import alias.action.RecurringAction; public class BeepAction extends RecurringAction { + + @Override + public AliasActionType getType() + { + return AliasActionType.BEEP; + } + @Override public void performAction( Alias alias, Message message ) { System.out.println( "\007" ); } + + @Override + public String toString() + { + return "Beep"; + } } diff --git a/src/alias/action/beep/BeepActionEditor.java b/src/alias/action/beep/BeepActionEditor.java index 7b5844f9..139f01e0 100644 --- a/src/alias/action/beep/BeepActionEditor.java +++ b/src/alias/action/beep/BeepActionEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,59 +17,77 @@ ******************************************************************************/ package alias.action.beep; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; -import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JPanel; +import javax.swing.JOptionPane; +import javax.swing.JSlider; import javax.swing.JSpinner; -import javax.swing.JTextArea; -import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import alias.action.AliasAction; import alias.action.RecurringAction.Interval; +import alias.id.priority.Priority; -public class BeepActionEditor extends JPanel implements ActionListener +public class BeepActionEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( BeepActionEditor.class ); - - private BeepActionNode mBeepActionNode; - private JTextField mTextSiteID; - private JComboBox mComboInterval; - private JSpinner mSpinnerPeriod; + private static final String HELP_TEXT = + "

    Beep Action

    " + + "This action will sound a beep according to the interval
    " + + "that you select:

    " + + "Once: Beep the first time the alias is active and
    " + + "never again

    " + + "Once, Reset After Delay: Beep once and suppress
    " + + "subsequent beeps for the specified period in seconds.
    " + + "After the reset period, beep again when the alias is active.

    " + + "Until Dismissed: Beep every period seconds until you
    " + + "click OK on the dialog that appears. Alerting is suppressed
    " + + "for 15 seconds after you click OK.
    " + + ""; - public BeepActionEditor( BeepActionNode beepActionNode ) + private JComboBox mComboInterval; + private JSlider mPeriodSlider; + private JLabel mPeriodSliderLabel; + + public BeepActionEditor( AliasAction aliasAction ) { - mBeepActionNode = beepActionNode; - - initGUI(); + init(); + + setItem( aliasAction ); } - private void initGUI() + public BeepAction getBeepAction() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "Action: Beep" ), "span,align center" ); + if( getItem() instanceof BeepAction ) + { + return (BeepAction)getItem(); + } - add( new JLabel( "Interval:" ) ); + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][]" ) ); + + add( new JLabel( "Beep Interval:" ) ); mComboInterval = new JComboBox( Interval.values() ); - - mComboInterval.setSelectedItem( mBeepActionNode.getBeepAction().getInterval() ); - + mComboInterval.setToolTipText( HELP_TEXT ); mComboInterval.addActionListener( new ActionListener() { @Override @@ -81,71 +99,97 @@ public class BeepActionEditor extends JPanel implements ActionListener if( selected != null ) { /* Enable/disable period spinner based on selection */ - mSpinnerPeriod.setEnabled( selected != Interval.ONCE ); + boolean enabled = selected != Interval.ONCE; - mBeepActionNode.getBeepAction().setInterval( selected ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSliderLabel.setEnabled( enabled ); } + + setModified( true ); } }); - add( mComboInterval, "wrap" ); - final SpinnerModel model = new SpinnerNumberModel( - mBeepActionNode.getBeepAction().getPeriod(), 1, 30, 1 ); - + final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); model.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { - mBeepActionNode.getBeepAction().setPeriod( (int)model.getValue() ); + setModified( true ); } } ); - mSpinnerPeriod = new JSpinner( model ); - - if( mBeepActionNode.getBeepAction().getInterval() == Interval.ONCE ) + mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); + mPeriodSlider.setMajorTickSpacing( 10 ); + mPeriodSlider.setMinorTickSpacing( 2 ); + mPeriodSlider.setPaintTicks( true ); + mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); + mPeriodSlider.setPaintLabels( true ); + mPeriodSlider.addChangeListener( new ChangeListener() { - mSpinnerPeriod.setEnabled( false ); + @Override + public void stateChanged( ChangeEvent e ) + { + mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); + setModified( true ); + } + } ); + mPeriodSlider.setToolTipText( HELP_TEXT ); + + mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); + add( mPeriodSliderLabel ); + add( mPeriodSlider, "wrap,grow" ); + + JLabel example = new JLabel( "Help ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( BeepActionEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); + } + + @Override + public void setItem( AliasAction item ) + { + super.setItem( item ); + + if( hasItem() ) + { + BeepAction beep = getBeepAction(); + + Interval interval = beep.getInterval(); + + mComboInterval.setSelectedItem( interval ); + + boolean enabled = interval != Interval.ONCE; + + mPeriodSliderLabel.setEnabled( enabled ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSlider.setValue( beep.getPeriod() ); } - add( new JLabel( "Period:" ) ); - add( mSpinnerPeriod, "wrap" ); - - StringBuilder sb = new StringBuilder(); - - sb.append( "This action will sound a beep according to the interval that you select.\n\n" ); - sb.append( "Once - Beep the first time the alias is active and never again.\n\n" ); - sb.append( "Once, Reset After Delay - Beep once and suppress subsequent beeps for the specified period in seconds. After the reset period, it will beep again when the alias is active.\n\n" ); - sb.append( "Until Dismissed - Beep every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." ); - - JTextArea description = new JTextArea( sb.toString() ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( BeepActionEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( BeepActionEditor.this ); - add( btnReset, "growx,push" ); + setModified( false ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + public void save() + { + if( hasItem() && isModified() ) { - mBeepActionNode.save(); - mBeepActionNode.show(); + BeepAction beep = getBeepAction(); + + beep.setInterval( (Interval)mComboInterval.getSelectedItem() ); + beep.setPeriod( mPeriodSlider.getValue() ); } - mBeepActionNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/action/beep/BeepActionNode.java b/src/alias/action/beep/BeepActionNode.java index ba5fccdf..96de565e 100644 --- a/src/alias/action/beep/BeepActionNode.java +++ b/src/alias/action/beep/BeepActionNode.java @@ -41,7 +41,7 @@ public class BeepActionNode extends AliasActionNode @Override public JPanel getEditor() { - return new BeepActionEditor( this ); + return null; } public BeepAction getBeepAction() diff --git a/src/alias/action/clip/ClipAction.java b/src/alias/action/clip/ClipAction.java index 30e4c113..dd338d96 100644 --- a/src/alias/action/clip/ClipAction.java +++ b/src/alias/action/clip/ClipAction.java @@ -13,6 +13,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.Alias; +import alias.action.AliasActionType; import alias.action.RecurringAction; public class ClipAction extends RecurringAction @@ -28,9 +29,14 @@ public class ClipAction extends RecurringAction { } + @Override + public AliasActionType getType() + { + return AliasActionType.CLIP; + } + public String getPath() { - return mFilePath; } @@ -87,4 +93,18 @@ public class ClipAction extends RecurringAction throw e; } } + + + @Override + public String toString() + { + if( mFilePath == null ) + { + return "Play Clip"; + } + else + { + return "Play Clip: " + mFilePath; + } + } } diff --git a/src/alias/action/clip/ClipActionEditor.java b/src/alias/action/clip/ClipActionEditor.java index e07b4aee..070e0ce9 100644 --- a/src/alias/action/clip/ClipActionEditor.java +++ b/src/alias/action/clip/ClipActionEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,9 +17,15 @@ ******************************************************************************/ package alias.action.clip; +import gui.editor.DocumentListenerEditor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; import javax.swing.JButton; @@ -27,9 +33,7 @@ import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextArea; +import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -37,43 +41,59 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import alias.action.AliasAction; import alias.action.RecurringAction.Interval; +import alias.action.script.ScriptActionEditor; -public class ClipActionEditor extends JPanel implements ActionListener +public class ClipActionEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ClipActionEditor.class ); - - private ClipActionNode mClipActionNode; + private static final String PLEASE_SELECT_A_FILE = "Please select a file"; + private static final String HELP_TEXT = + "

    Clip Action

    " + + "This action will play an audio clip according to the interval
    " + + "that you select:

    " + + "Once: play the first time the alias is active and
    " + + "never again

    " + + "Once, Reset After Delay: play once and suppress
    " + + "subsequent runs for the specified period in seconds.
    " + + "After the reset period, play again when the alias is active.

    " + + "Until Dismissed: play every period seconds until you
    " + + "click OK on the dialog that appears. Alerting is suppressed
    " + + "for 15 seconds after you click OK.
    " + + ""; + private JComboBox mComboInterval; - private JSpinner mSpinnerPeriod; + private JSlider mPeriodSlider; + private JLabel mPeriodSliderLabel; private JTextField mTextFilePath; - public ClipActionEditor( ClipActionNode clipActionNode ) + public ClipActionEditor( AliasAction aliasAction ) { - mClipActionNode = clipActionNode; - - initGUI(); + init(); + + setItem( aliasAction ); } - private void initGUI() + public ClipAction getClipAction() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "Action: Audio Clip" ), "span,align center" ); + if( getItem() instanceof ClipAction ) + { + return (ClipAction)getItem(); + } - add( new JLabel( "Interval:" ) ); + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) ); + + add( new JLabel( "Play Interval:" ) ); mComboInterval = new JComboBox( Interval.values() ); - - mComboInterval.setSelectedItem( mClipActionNode.getClipAction().getInterval() ); - + mComboInterval.setToolTipText( HELP_TEXT ); mComboInterval.addActionListener( new ActionListener() { @Override @@ -84,37 +104,51 @@ public class ClipActionEditor extends JPanel implements ActionListener if( selected != null ) { - /* Enable/disable period spinner based on selection */ - mSpinnerPeriod.setEnabled( selected != Interval.ONCE ); + /* Enable/disable period slider based on selection */ + boolean enabled = selected != Interval.ONCE; - mClipActionNode.getClipAction().setInterval( selected ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSliderLabel.setEnabled( enabled ); } + + setModified( true ); } }); - - add( mComboInterval, "wrap" ); + add( mComboInterval ); - final SpinnerModel model = new SpinnerNumberModel( - mClipActionNode.getClipAction().getPeriod(), 1, 30, 1 ); - + final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); model.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { - mClipActionNode.getClipAction().setPeriod( (int)model.getValue() ); + setModified( true ); } } ); - mSpinnerPeriod = new JSpinner( model ); - - if( mClipActionNode.getClipAction().getInterval() == Interval.ONCE ) + mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); + mPeriodSlider.setMajorTickSpacing( 10 ); + mPeriodSlider.setMinorTickSpacing( 2 ); + mPeriodSlider.setPaintTicks( true ); + mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); + mPeriodSlider.setPaintLabels( true ); + mPeriodSlider.addChangeListener( new ChangeListener() { - mSpinnerPeriod.setEnabled( false ); - } + @Override + public void stateChanged( ChangeEvent e ) + { + mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); + setModified( true ); + } + } ); + mPeriodSlider.setToolTipText( HELP_TEXT ); - add( new JLabel( "Period:" ) ); - add( mSpinnerPeriod, "wrap" ); + mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); + add( mPeriodSliderLabel ); + add( mPeriodSlider, "wrap,grow" ); + + mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE ); + add( mTextFilePath, "growx,span" ); JButton fileButton = new JButton( "File" ); @@ -131,88 +165,134 @@ public class ClipActionEditor extends JPanel implements ActionListener { File file = chooser.getSelectedFile(); - mClipActionNode.getClipAction().setPath( file.getAbsolutePath() ); mTextFilePath.setText( file.getAbsolutePath() ); + + setModified( true ); } } } ); - add( fileButton ); + add( fileButton, "grow" ); JButton testButton = new JButton( "Test" ); - testButton.addActionListener( new ActionListener() + + testButton.addActionListener( new ActionListener() { @Override - public void actionPerformed( ActionEvent actionEvent ) + public void actionPerformed( ActionEvent e ) { - String path = mClipActionNode.getClipAction().getPath(); - - if( path == null || path.trim().isEmpty() ) + if( hasItem() ) { - JOptionPane.showMessageDialog( ClipActionEditor.this, - "Please select an audio file", "Please select file", - JOptionPane.ERROR_MESSAGE ); - } - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() + String path = mTextFilePath.getText(); + + if( path == null || path.trim().isEmpty() ) { - try - { - mClipActionNode.getClipAction().play(); - } - catch( Exception e ) - { - JOptionPane.showMessageDialog( ClipActionEditor.this, - "Couldn't play audio clip [" + e.getMessage() + "]", - "Error", JOptionPane.ERROR_MESSAGE ); - } + JOptionPane.showMessageDialog( ClipActionEditor.this, + "Please select an audio file", PLEASE_SELECT_A_FILE, + JOptionPane.ERROR_MESSAGE ); } - } ); + else + { + if( isModified() ) + { + int option = JOptionPane.showConfirmDialog( + ClipActionEditor.this, + "Settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + save(); + } + } + + new Thread( new Runnable() + { + @Override + public void run() + { + try + { + getClipAction().play(); + } + catch( Exception e1 ) + { + JOptionPane.showMessageDialog( ClipActionEditor.this, + "Couldn't play audio clip [" + e1.getMessage() + "]", + "Error", JOptionPane.ERROR_MESSAGE ); + } + } + }).start(); + } + } } } ); - - add( testButton, "wrap" ); - - mTextFilePath = new JTextField( mClipActionNode.getClipAction().getPath() ); - add( mTextFilePath, "growx,span" ); - StringBuilder sb = new StringBuilder(); + add( testButton, "grow,wrap" ); - sb.append( "This action will play the audio clip according to the interval that you select.\n\n" ); - sb.append( "Once - Play the first time the alias is active and never again.\n\n" ); - sb.append( "Once, Reset After Delay - Play once and suppress subsequent plays for the specified period in seconds. After the reset period, it will play again when the alias is active.\n\n" ); - sb.append( "Until Dismissed - Play audio clip every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." ); + JLabel example = new JLabel( "Help ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( ClipActionEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); + } + + @Override + public void setItem( AliasAction item ) + { + super.setItem( item ); - JTextArea description = new JTextArea( sb.toString() ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ClipActionEditor.this ); - add( btnSave, "growx,push" ); + if( hasItem() ) + { + ClipAction clip = getClipAction(); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ClipActionEditor.this ); - add( btnReset, "growx,push" ); + Interval interval = clip.getInterval(); + + mComboInterval.setSelectedItem( interval ); + + boolean enabled = interval != Interval.ONCE; + + mPeriodSliderLabel.setEnabled( enabled ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSlider.setValue( clip.getPeriod() ); + + String filepath = clip.getPath(); + + if( filepath != null && !filepath.isEmpty() ) + { + mTextFilePath.setText( filepath ); + } + else + { + mTextFilePath.setText( PLEASE_SELECT_A_FILE ); + } + } + + setModified( false ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + public void save() + { + if( hasItem() && isModified() ) { - mClipActionNode.save(); - mClipActionNode.show(); + ClipAction script = getClipAction(); + + script.setInterval( (Interval)mComboInterval.getSelectedItem() ); + script.setPeriod( mPeriodSlider.getValue() ); + script.setPath( mTextFilePath.getText() ); } - mClipActionNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/action/clip/ClipActionNode.java b/src/alias/action/clip/ClipActionNode.java index 15f7005b..3e42a432 100644 --- a/src/alias/action/clip/ClipActionNode.java +++ b/src/alias/action/clip/ClipActionNode.java @@ -41,7 +41,7 @@ public class ClipActionNode extends AliasActionNode @Override public JPanel getEditor() { - return new ClipActionEditor( this ); + return null; } public ClipAction getClipAction() diff --git a/src/alias/action/script/ScriptAction.java b/src/alias/action/script/ScriptAction.java index de94ffc1..8dcfb55f 100644 --- a/src/alias/action/script/ScriptAction.java +++ b/src/alias/action/script/ScriptAction.java @@ -9,6 +9,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.Alias; +import alias.action.AliasActionType; import alias.action.RecurringAction; public class ScriptAction extends RecurringAction @@ -21,6 +22,12 @@ public class ScriptAction extends RecurringAction { } + @Override + public AliasActionType getType() + { + return AliasActionType.SCRIPT; + } + public String getScript() { return mScript; @@ -75,4 +82,17 @@ public class ScriptAction extends RecurringAction } } } + + @Override + public String toString() + { + if( mScript == null ) + { + return "Run Script"; + } + else + { + return "Run Script: " + mScript; + } + } } diff --git a/src/alias/action/script/ScriptActionEditor.java b/src/alias/action/script/ScriptActionEditor.java index 22f2c77d..ea7e181e 100644 --- a/src/alias/action/script/ScriptActionEditor.java +++ b/src/alias/action/script/ScriptActionEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,9 +17,16 @@ ******************************************************************************/ package alias.action.script; +import gui.editor.DocumentListenerEditor; +import gui.editor.Editor; + +import java.awt.Color; +import java.awt.Cursor; import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.io.File; import javax.swing.JButton; @@ -27,9 +34,7 @@ import javax.swing.JComboBox; import javax.swing.JFileChooser; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextArea; +import javax.swing.JSlider; import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; @@ -37,43 +42,59 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - +import alias.action.AliasAction; import alias.action.RecurringAction.Interval; +import alias.action.clip.ClipActionEditor; -public class ScriptActionEditor extends JPanel implements ActionListener +public class ScriptActionEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ScriptActionEditor.class ); - - private ScriptActionNode mScriptActionNode; + private static final String PLEASE_SELECT_A_FILE = "Please select a file"; + private static final String HELP_TEXT = + "

    Script Action

    " + + "This action will run a script according to the interval
    " + + "that you select:

    " + + "Once: run the first time the alias is active and
    " + + "never again

    " + + "Once, Reset After Delay: run once and suppress
    " + + "subsequent runs for the specified period in seconds.
    " + + "After the reset period, run again when the alias is active.

    " + + "Until Dismissed: run every period seconds until you
    " + + "click OK on the dialog that appears. Alerting is suppressed
    " + + "for 15 seconds after you click OK.
    " + + ""; + private JComboBox mComboInterval; - private JSpinner mSpinnerPeriod; + private JSlider mPeriodSlider; + private JLabel mPeriodSliderLabel; private JTextField mTextFilePath; - public ScriptActionEditor( ScriptActionNode clipActionNode ) + public ScriptActionEditor( AliasAction aliasAction ) { - mScriptActionNode = clipActionNode; - - initGUI(); + init(); + + setItem( aliasAction ); } - private void initGUI() + public ScriptAction getScriptAction() { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "Action: Script" ), "span,align center" ); + if( getItem() instanceof ScriptAction ) + { + return (ScriptAction)getItem(); + } - add( new JLabel( "Interval:" ) ); + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][]" ) ); + + add( new JLabel( "Run Interval:" ) ); mComboInterval = new JComboBox( Interval.values() ); - - mComboInterval.setSelectedItem( mScriptActionNode.getScriptAction().getInterval() ); - + mComboInterval.setToolTipText( HELP_TEXT ); mComboInterval.addActionListener( new ActionListener() { @Override @@ -84,37 +105,51 @@ public class ScriptActionEditor extends JPanel implements ActionListener if( selected != null ) { - /* Enable/disable period spinner based on selection */ - mSpinnerPeriod.setEnabled( selected != Interval.ONCE ); + /* Enable/disable period slider based on selection */ + boolean enabled = selected != Interval.ONCE; - mScriptActionNode.getScriptAction().setInterval( selected ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSliderLabel.setEnabled( enabled ); } + + setModified( true ); } }); - - add( mComboInterval, "wrap" ); + add( mComboInterval ); - final SpinnerModel model = new SpinnerNumberModel( - mScriptActionNode.getScriptAction().getPeriod(), 1, 30, 1 ); - + final SpinnerModel model = new SpinnerNumberModel( 1, 1, 30, 1 ); model.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { - mScriptActionNode.getScriptAction().setPeriod( (int)model.getValue() ); + setModified( true ); } } ); - mSpinnerPeriod = new JSpinner( model ); - - if( mScriptActionNode.getScriptAction().getInterval() == Interval.ONCE ) + mPeriodSlider = new JSlider( JSlider.HORIZONTAL, 1, 60, 1 ); + mPeriodSlider.setMajorTickSpacing( 10 ); + mPeriodSlider.setMinorTickSpacing( 2 ); + mPeriodSlider.setPaintTicks( true ); + mPeriodSlider.setLabelTable( mPeriodSlider.createStandardLabels( 10, 10 ) ); + mPeriodSlider.setPaintLabels( true ); + mPeriodSlider.addChangeListener( new ChangeListener() { - mSpinnerPeriod.setEnabled( false ); - } + @Override + public void stateChanged( ChangeEvent e ) + { + mPeriodSliderLabel.setText( "Period: " + mPeriodSlider.getValue() ); + setModified( true ); + } + } ); + mPeriodSlider.setToolTipText( HELP_TEXT ); - add( new JLabel( "Period:" ) ); - add( mSpinnerPeriod, "wrap" ); + mPeriodSliderLabel = new JLabel( "Period: " + mPeriodSlider.getValue() + " " ); + add( mPeriodSliderLabel ); + add( mPeriodSlider, "wrap,grow" ); + + mTextFilePath = new JTextField( PLEASE_SELECT_A_FILE ); + add( mTextFilePath, "growx,span" ); JButton fileButton = new JButton( "File" ); @@ -131,13 +166,14 @@ public class ScriptActionEditor extends JPanel implements ActionListener { File file = chooser.getSelectedFile(); - mScriptActionNode.getScriptAction().setScript( file.getAbsolutePath() ); mTextFilePath.setText( file.getAbsolutePath() ); + + setModified( true ); } } } ); - add( fileButton ); + add( fileButton, "grow" ); JButton testButton = new JButton( "Test" ); testButton.addActionListener( new ActionListener() @@ -145,74 +181,115 @@ public class ScriptActionEditor extends JPanel implements ActionListener @Override public void actionPerformed( ActionEvent actionEvent ) { - String script = mScriptActionNode.getScriptAction().getScript(); + String script = mTextFilePath.getText(); if( script == null || script.trim().isEmpty() ) { JOptionPane.showMessageDialog( ScriptActionEditor.this, - "Please select a script file", "Please select file", + "Please select a script file", PLEASE_SELECT_A_FILE, JOptionPane.ERROR_MESSAGE ); } - - EventQueue.invokeLater( new Runnable() + else { - @Override - public void run() + if( isModified() ) { - try + int option = JOptionPane.showConfirmDialog( + ScriptActionEditor.this, + "Settings have changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) { - mScriptActionNode.getScriptAction().play(); - } - catch( Exception e ) - { - JOptionPane.showMessageDialog( ScriptActionEditor.this, - "Couldn't run script [" + e.getMessage() + "]", - "Error", JOptionPane.ERROR_MESSAGE ); + save(); } } - } ); + + new Thread( new Runnable() + { + @Override + public void run() + { + try + { + getScriptAction().play(); + } + catch( Exception e1 ) + { + JOptionPane.showMessageDialog( ScriptActionEditor.this, + e1.getMessage(), "Error", JOptionPane.ERROR_MESSAGE ); + } + } + }).start(); + } + } } ); - add( testButton, "wrap" ); + add( testButton, "grow,wrap" ); - mTextFilePath = new JTextField( mScriptActionNode.getScriptAction().getScript() ); - add( mTextFilePath, "growx,span" ); + JLabel example = new JLabel( "Help ..." ); + example.setForeground( Color.BLUE.brighter() ); + example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + example.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + JOptionPane.showMessageDialog( ScriptActionEditor.this, + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( example ); + } + + @Override + public void setItem( AliasAction item ) + { + super.setItem( item ); + + if( hasItem() ) + { + ScriptAction script = getScriptAction(); - StringBuilder sb = new StringBuilder(); - - sb.append( "This action will run the script according to the interval that you select.\n\n" ); - sb.append( "Once - Run script the first time the alias is active and never again.\n\n" ); - sb.append( "Once, Reset After Delay - Run once and suppress subsequent runs for the specified period in seconds. After the reset period, it will play again when the alias is active.\n\n" ); - sb.append( "Until Dismissed - Run script every period seconds until you click OK on the dialog that appears. Alerting is suppressed for 15 seconds after you click OK." ); - - JTextArea description = new JTextArea( sb.toString() ); - - description.setLineWrap( true ); - description.setBackground( getBackground() ); - - add( description, "growx,span" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ScriptActionEditor.this ); - add( btnSave, "growx,push" ); + Interval interval = script.getInterval(); + + mComboInterval.setSelectedItem( interval ); - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ScriptActionEditor.this ); - add( btnReset, "growx,push" ); + boolean enabled = interval != Interval.ONCE; + + mPeriodSliderLabel.setEnabled( enabled ); + mPeriodSlider.setEnabled( enabled ); + mPeriodSlider.setValue( script.getPeriod() ); + + String filepath = script.getScript(); + + if( filepath != null && !filepath.isEmpty() ) + { + mTextFilePath.setText( filepath ); + } + else + { + mTextFilePath.setText( PLEASE_SELECT_A_FILE ); + } + } + + setModified( false ); } @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) + public void save() + { + if( hasItem() && isModified() ) { - mScriptActionNode.save(); - mScriptActionNode.show(); + ScriptAction script = getScriptAction(); + + script.setInterval( (Interval)mComboInterval.getSelectedItem() ); + script.setPeriod( mPeriodSlider.getValue() ); + script.setScript( mTextFilePath.getText() ); } - mScriptActionNode.refresh(); - } + setModified( false ); + } } diff --git a/src/alias/action/script/ScriptActionNode.java b/src/alias/action/script/ScriptActionNode.java index cbe25b45..74d20a44 100644 --- a/src/alias/action/script/ScriptActionNode.java +++ b/src/alias/action/script/ScriptActionNode.java @@ -41,7 +41,7 @@ public class ScriptActionNode extends AliasActionNode @Override public JPanel getEditor() { - return new ScriptActionEditor( this ); + return null; } public ScriptAction getScriptAction() diff --git a/src/alias/AliasID.java b/src/alias/id/AliasID.java similarity index 98% rename from src/alias/AliasID.java rename to src/alias/id/AliasID.java index a4a8ec7f..63497c91 100644 --- a/src/alias/AliasID.java +++ b/src/alias/id/AliasID.java @@ -15,11 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package alias.id; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import alias.Group; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; import alias.id.lojack.LoJackFunctionAndID; diff --git a/src/alias/AliasIDNode.java b/src/alias/id/AliasIDNode.java similarity index 98% rename from src/alias/AliasIDNode.java rename to src/alias/id/AliasIDNode.java index 3852b44d..adaf3b36 100644 --- a/src/alias/AliasIDNode.java +++ b/src/alias/id/AliasIDNode.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package alias.id; import playlist.PlaylistManager; import controller.ConfigurableNode; diff --git a/src/alias/AliasIDType.java b/src/alias/id/AliasIDType.java similarity index 86% rename from src/alias/AliasIDType.java rename to src/alias/id/AliasIDType.java index a56d5b87..7417b66a 100644 --- a/src/alias/AliasIDType.java +++ b/src/alias/id/AliasIDType.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package alias.id; public enum AliasIDType { @@ -26,11 +26,14 @@ public enum AliasIDType MDC1200( "MDC-1200" ), MIN( "Passport MIN" ), MPT1327( "MPT-1327" ), - NonRecordable( "Non-Recordable" ), - Priority( "Audio Priority" ), Site( "Site" ), Status( "Status" ), - Talkgroup( "Talkgroup" ); + Talkgroup( "Talkgroup" ), + + Invert( "Audio Inversion" ), + Priority( "Audio Priority" ), + NonRecordable( "Audio Non-Recordable" ), + Streaming( "Audio Streaming" ); private String mLabel; diff --git a/src/alias/AliasIdentifierEditor.java b/src/alias/id/AliasIdentifierEditor.java similarity index 76% rename from src/alias/AliasIdentifierEditor.java rename to src/alias/id/AliasIdentifierEditor.java index 3a8fca14..f2b89a48 100644 --- a/src/alias/AliasIdentifierEditor.java +++ b/src/alias/id/AliasIdentifierEditor.java @@ -1,4 +1,24 @@ -package alias; +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package alias.id; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; import java.awt.EventQueue; import java.awt.event.ActionEvent; @@ -27,9 +47,13 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import alias.Alias; +import alias.AliasEvent; import alias.AliasEvent.Event; +import alias.AliasFactory; +import alias.AliasModel; -public class AliasIdentifierEditor extends AliasConfigurationEditor +public class AliasIdentifierEditor extends Editor { private static final long serialVersionUID = 1L; @@ -40,10 +64,8 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor private JButton mNewIDButton; private JButton mCloneIDButton; private JButton mDeleteIDButton; - private EditorContainer mEditorContainer = new EditorContainer(); - private Alias mAlias; private AliasModel mAliasModel; public AliasIdentifierEditor( AliasModel model ) @@ -57,7 +79,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[][grow,fill][]" ) ); - mAliasIDList.setVisibleRowCount( 4 ); + mAliasIDList.setVisibleRowCount( 6 ); mAliasIDList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); mAliasIDList.setLayoutOrientation( JList.VERTICAL ); mAliasIDList.addListSelectionListener( new ListSelectionListener() @@ -95,7 +117,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor add( mEditorContainer, "span,grow" ); mNewIDButton = new JButton( "New ..." ); - + mNewIDButton.setToolTipText( "Create a new Alias Identifier" ); mNewIDButton.addMouseListener( new MouseAdapter() { @Override @@ -115,7 +137,6 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor menu.add( new AddAliasIdentifierItem( AliasIDType.Talkgroup ) ); menu.addSeparator(); menu.add( new AddAliasIdentifierItem( AliasIDType.Priority ) ); - menu.addSeparator(); menu.add( new AddAliasIdentifierItem( AliasIDType.NonRecordable ) ); menu.show( e.getComponent(), e.getX(), e.getY() ); @@ -125,6 +146,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor add( mNewIDButton ); mCloneIDButton = new JButton( "Clone" ); + mCloneIDButton.setToolTipText( "Create a copy of the currently selected identifier" ); mCloneIDButton.setEnabled( false ); mCloneIDButton.addActionListener( new ActionListener() { @@ -147,6 +169,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor add( mCloneIDButton ); mDeleteIDButton = new JButton( "Delete" ); + mDeleteIDButton.setToolTipText( "Delete the currently selected identifier" ); mDeleteIDButton.setEnabled( false ); mDeleteIDButton.addActionListener( new ActionListener() { @@ -161,13 +184,15 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor "Do you want to delete [" + selected.toString() + "]", "Are you sure?", JOptionPane.YES_NO_OPTION ); - if( choice == JOptionPane.YES_OPTION ) + if( choice == JOptionPane.YES_OPTION && hasItem() ) { - mAlias.removeAliasID( selected ); + Alias alias = getItem(); - mAliasModel.broadcast( new AliasEvent( mAlias, Event.CHANGE ) ); + alias.removeAliasID( selected ); + + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); - setAlias( mAlias ); + setItem( alias ); } } } @@ -176,11 +201,11 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor } @Override - public void setAlias( Alias alias ) + public void setItem( Alias alias ) { - mAlias = alias; + super.setItem( alias ); - if( mAlias == null || mAlias.getId().isEmpty() ) + if( alias == null || alias.getId().isEmpty() ) { mAliasIDList.setModel( EMPTY_MODEL ); } @@ -188,7 +213,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor { DefaultListModel model = new DefaultListModel(); - List ids = mAlias.getId(); + List ids = alias.getId(); Collections.sort( ids, new Comparator() { @@ -216,16 +241,19 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor if( mEditorContainer.isModified() ) { mEditorContainer.save(); + + if( hasItem() ) + { + mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); + } } - - mAliasModel.broadcast( new AliasEvent( mAlias, Event.CHANGE ) ); } public class EditorContainer extends JPanel { private static final long serialVersionUID = 1L; - private ComponentEditor mEditor = new EmptyAliasIDEditor(); + private Editor mEditor = new EmptyEditor<>(); public EditorContainer() { @@ -273,6 +301,7 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor } removeAll(); + //This will always give us an editor mEditor = AliasFactory.getEditor( aliasID ); add( mEditor ); @@ -283,16 +312,18 @@ public class AliasIdentifierEditor extends AliasConfigurationEditor private void addAliasIDToList( final AliasID id ) { - if( id != null && mAlias != null ) + if( id != null && hasItem() ) { - mAlias.addAliasID( id ); + final Alias alias = getItem(); + + alias.addAliasID( id ); EventQueue.invokeLater( new Runnable() { @Override public void run() { - setAlias( mAlias ); + setItem( alias ); mAliasIDList.setSelectedValue( id, true ); } diff --git a/src/alias/id/esn/ESNEditor.java b/src/alias/id/esn/ESNEditor.java index 2aa59876..5973c291 100644 --- a/src/alias/id/esn/ESNEditor.java +++ b/src/alias/id/esn/ESNEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.esn; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -24,15 +26,12 @@ import java.awt.event.MouseEvent; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JTextArea; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; -import alias.id.talkgroup.TalkgroupIDEditor; +import alias.id.AliasID; -public class ESNEditor extends ComponentEditor +public class ESNEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -47,11 +46,9 @@ public class ESNEditor extends ComponentEditor public ESNEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -81,18 +78,18 @@ public class ESNEditor extends ComponentEditor public Esn getEsn() { - if( getComponent() instanceof Esn ) + if( getItem() instanceof Esn ) { - return (Esn)getComponent(); + return (Esn)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); Esn esn = getEsn(); diff --git a/src/alias/id/esn/ESNNode.java b/src/alias/id/esn/ESNNode.java index a814811e..b75dc306 100644 --- a/src/alias/id/esn/ESNNode.java +++ b/src/alias/id/esn/ESNNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class ESNNode extends AliasIDNode { diff --git a/src/alias/id/esn/Esn.java b/src/alias/id/esn/Esn.java index 09198031..91d5396d 100644 --- a/src/alias/id/esn/Esn.java +++ b/src/alias/id/esn/Esn.java @@ -19,8 +19,8 @@ package alias.id.esn; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class Esn extends AliasID { diff --git a/src/alias/id/fleetsync/FleetsyncID.java b/src/alias/id/fleetsync/FleetsyncID.java index ca92a75c..57de648e 100644 --- a/src/alias/id/fleetsync/FleetsyncID.java +++ b/src/alias/id/fleetsync/FleetsyncID.java @@ -19,8 +19,8 @@ package alias.id.fleetsync; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class FleetsyncID extends AliasID diff --git a/src/alias/id/fleetsync/FleetsyncIDEditor.java b/src/alias/id/fleetsync/FleetsyncIDEditor.java index 8bb1bc06..b4dbc7ba 100644 --- a/src/alias/id/fleetsync/FleetsyncIDEditor.java +++ b/src/alias/id/fleetsync/FleetsyncIDEditor.java @@ -17,30 +17,27 @@ ******************************************************************************/ package alias.id.fleetsync; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; +import net.miginfocom.swing.MigLayout; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import controller.channel.Channel; -import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; -import alias.id.esn.ESNEditor; +import alias.id.AliasID; -public class FleetsyncIDEditor extends ComponentEditor +public class FleetsyncIDEditor extends DocumentListenerEditor { private final static Logger mLog = LoggerFactory.getLogger( FleetsyncIDEditor.class ); @@ -56,11 +53,9 @@ public class FleetsyncIDEditor extends ComponentEditor public FleetsyncIDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -104,18 +99,18 @@ public class FleetsyncIDEditor extends ComponentEditor public FleetsyncID getFleetsyncID() { - if( getComponent() instanceof FleetsyncID ) + if( getItem() instanceof FleetsyncID ) { - return (FleetsyncID)getComponent(); + return (FleetsyncID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); FleetsyncID fleetsync = getFleetsyncID(); diff --git a/src/alias/id/fleetsync/FleetsyncIDNode.java b/src/alias/id/fleetsync/FleetsyncIDNode.java index 280c44a8..aae77b47 100644 --- a/src/alias/id/fleetsync/FleetsyncIDNode.java +++ b/src/alias/id/fleetsync/FleetsyncIDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class FleetsyncIDNode extends AliasIDNode { diff --git a/src/alias/id/lojack/LoJackFunctionAndID.java b/src/alias/id/lojack/LoJackFunctionAndID.java index 99ee74b1..065db0f4 100644 --- a/src/alias/id/lojack/LoJackFunctionAndID.java +++ b/src/alias/id/lojack/LoJackFunctionAndID.java @@ -21,8 +21,8 @@ import javax.xml.bind.annotation.XmlAttribute; import module.decode.lj1200.LJ1200Message; import module.decode.lj1200.LJ1200Message.Function; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class LoJackFunctionAndID extends AliasID { diff --git a/src/alias/id/lojack/LoJackIDEditor.java b/src/alias/id/lojack/LoJackIDEditor.java index 6fcf9ad4..aa6ae805 100644 --- a/src/alias/id/lojack/LoJackIDEditor.java +++ b/src/alias/id/lojack/LoJackIDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.lojack; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.ActionEvent; @@ -34,10 +36,9 @@ import javax.swing.text.MaskFormatter; import module.decode.lj1200.LJ1200Message; import module.decode.lj1200.LJ1200Message.Function; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class LoJackIDEditor extends ComponentEditor +public class LoJackIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -61,11 +62,9 @@ public class LoJackIDEditor extends ComponentEditor public LoJackIDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -121,18 +120,18 @@ public class LoJackIDEditor extends ComponentEditor public LoJackFunctionAndID getLoJackID() { - if( getComponent() instanceof LoJackFunctionAndID ) + if( getItem() instanceof LoJackFunctionAndID ) { - return (LoJackFunctionAndID)getComponent(); + return (LoJackFunctionAndID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); LoJackFunctionAndID lojack = getLoJackID(); diff --git a/src/alias/id/lojack/LoJackIDNode.java b/src/alias/id/lojack/LoJackIDNode.java index b5e65b7f..76588b38 100644 --- a/src/alias/id/lojack/LoJackIDNode.java +++ b/src/alias/id/lojack/LoJackIDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class LoJackIDNode extends AliasIDNode { diff --git a/src/alias/id/mdc/MDC1200ID.java b/src/alias/id/mdc/MDC1200ID.java index cf866e35..af0ed12e 100644 --- a/src/alias/id/mdc/MDC1200ID.java +++ b/src/alias/id/mdc/MDC1200ID.java @@ -19,8 +19,8 @@ package alias.id.mdc; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class MDC1200ID extends AliasID diff --git a/src/alias/id/mdc/MDC1200IDEditor.java b/src/alias/id/mdc/MDC1200IDEditor.java index cae6ae6d..eb092e35 100644 --- a/src/alias/id/mdc/MDC1200IDEditor.java +++ b/src/alias/id/mdc/MDC1200IDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.mdc; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -29,10 +31,9 @@ import javax.swing.JTextField; import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class MDC1200IDEditor extends ComponentEditor +public class MDC1200IDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -47,11 +48,9 @@ public class MDC1200IDEditor extends ComponentEditor public MDC1200IDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -94,18 +93,18 @@ public class MDC1200IDEditor extends ComponentEditor public MDC1200ID getMDC1200ID() { - if( getComponent() instanceof MDC1200ID ) + if( getItem() instanceof MDC1200ID ) { - return (MDC1200ID)getComponent(); + return (MDC1200ID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); MDC1200ID mdc = getMDC1200ID(); diff --git a/src/alias/id/mdc/MDC1200IDNode.java b/src/alias/id/mdc/MDC1200IDNode.java index 0af5fae0..06309d68 100644 --- a/src/alias/id/mdc/MDC1200IDNode.java +++ b/src/alias/id/mdc/MDC1200IDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class MDC1200IDNode extends AliasIDNode { diff --git a/src/alias/id/mobileID/MINEditor.java b/src/alias/id/mobileID/MINEditor.java index 972a6764..f873e70e 100644 --- a/src/alias/id/mobileID/MINEditor.java +++ b/src/alias/id/mobileID/MINEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.mobileID; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -25,16 +27,13 @@ import java.awt.event.MouseEvent; import javax.swing.JFormattedTextField; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; -import alias.id.esn.ESNEditor; +import alias.id.AliasID; -public class MINEditor extends ComponentEditor +public class MINEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -49,11 +48,9 @@ public class MINEditor extends ComponentEditor public MINEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -96,18 +93,18 @@ public class MINEditor extends ComponentEditor public Min getMin() { - if( getComponent() instanceof Min ) + if( getItem() instanceof Min ) { - return (Min)getComponent(); + return (Min)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); Min min = getMin(); diff --git a/src/alias/id/mobileID/MINNode.java b/src/alias/id/mobileID/MINNode.java index 3f43fca2..2c04c0e8 100644 --- a/src/alias/id/mobileID/MINNode.java +++ b/src/alias/id/mobileID/MINNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class MINNode extends AliasIDNode { diff --git a/src/alias/id/mobileID/Min.java b/src/alias/id/mobileID/Min.java index ca6b02dd..c6f8e4c2 100644 --- a/src/alias/id/mobileID/Min.java +++ b/src/alias/id/mobileID/Min.java @@ -19,8 +19,8 @@ package alias.id.mobileID; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; /** * Mobile ID Number diff --git a/src/alias/id/mpt1327/MPT1327ID.java b/src/alias/id/mpt1327/MPT1327ID.java index 5aa4c463..57535dbb 100644 --- a/src/alias/id/mpt1327/MPT1327ID.java +++ b/src/alias/id/mpt1327/MPT1327ID.java @@ -19,8 +19,8 @@ package alias.id.mpt1327; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class MPT1327ID extends AliasID { diff --git a/src/alias/id/mpt1327/MPT1327IDEditor.java b/src/alias/id/mpt1327/MPT1327IDEditor.java index a46709b8..1e9283c8 100644 --- a/src/alias/id/mpt1327/MPT1327IDEditor.java +++ b/src/alias/id/mpt1327/MPT1327IDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.mpt1327; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -29,10 +31,9 @@ import javax.swing.JTextField; import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class MPT1327IDEditor extends ComponentEditor +public class MPT1327IDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -48,11 +49,9 @@ public class MPT1327IDEditor extends ComponentEditor public MPT1327IDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -96,18 +95,18 @@ public class MPT1327IDEditor extends ComponentEditor public MPT1327ID getMPT1327ID() { - if( getComponent() instanceof MPT1327ID ) + if( getItem() instanceof MPT1327ID ) { - return (MPT1327ID)getComponent(); + return (MPT1327ID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); MPT1327ID mpt = getMPT1327ID(); diff --git a/src/alias/id/mpt1327/MPT1327IDNode.java b/src/alias/id/mpt1327/MPT1327IDNode.java index 04cda43a..da648bfe 100644 --- a/src/alias/id/mpt1327/MPT1327IDNode.java +++ b/src/alias/id/mpt1327/MPT1327IDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class MPT1327IDNode extends AliasIDNode { diff --git a/src/alias/id/nonrecordable/NonRecordable.java b/src/alias/id/nonrecordable/NonRecordable.java index 423ccd56..97128880 100644 --- a/src/alias/id/nonrecordable/NonRecordable.java +++ b/src/alias/id/nonrecordable/NonRecordable.java @@ -1,7 +1,7 @@ package alias.id.nonrecordable; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class NonRecordable extends AliasID { @@ -20,6 +20,6 @@ public class NonRecordable extends AliasID @Override public String toString() { - return "Non-Recordable"; + return "Audio Non-Recordable"; } } diff --git a/src/alias/id/nonrecordable/NonRecordableEditor.java b/src/alias/id/nonrecordable/NonRecordableEditor.java index 54aab852..35578a51 100644 --- a/src/alias/id/nonrecordable/NonRecordableEditor.java +++ b/src/alias/id/nonrecordable/NonRecordableEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.nonrecordable; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -26,26 +28,23 @@ import javax.swing.JLabel; import javax.swing.JOptionPane; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class NonRecordableEditor extends ComponentEditor +public class NonRecordableEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    Non-Recordable Example

    " + + "

    Audio Non-Recordable Example

    " + "This identifies an alias as non-recordable.

    " - + "The alias can still be monitored but it will not be recorded
    " + + "The alias can still be monitored but associated audio will not be recorded
    " + "when recording is enabled for the decoding channel."; public NonRecordableEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -63,17 +62,12 @@ public class NonRecordableEditor extends ComponentEditor public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( NonRecordableEditor.this, - HELP_TEXT, "Examples", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); } } ); add( example ); } - @Override - public void setComponent( AliasID aliasID ) - { - } - @Override public void save() { diff --git a/src/alias/id/nonrecordable/NonRecordableNode.java b/src/alias/id/nonrecordable/NonRecordableNode.java index 2a9a2fa4..81f756bb 100644 --- a/src/alias/id/nonrecordable/NonRecordableNode.java +++ b/src/alias/id/nonrecordable/NonRecordableNode.java @@ -26,9 +26,9 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasID; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasID; +import alias.id.AliasIDNode; public class NonRecordableNode extends AliasIDNode { diff --git a/src/alias/id/priority/Priority.java b/src/alias/id/priority/Priority.java index d15a75f0..e29be8a0 100644 --- a/src/alias/id/priority/Priority.java +++ b/src/alias/id/priority/Priority.java @@ -19,8 +19,8 @@ package alias.id.priority; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; /** * Specifies an (audio) priority level for this alias. @@ -60,7 +60,7 @@ public class Priority extends AliasID public String toString() { - return "Priority: " + ( isDoNotMonitor() ? "Do Not Monitor" : mPriority ); + return "Audio Priority: " + ( isDoNotMonitor() ? "Do Not Monitor" : mPriority ); } @Override diff --git a/src/alias/id/priority/PriorityEditor.java b/src/alias/id/priority/PriorityEditor.java index 1190d2ec..db3868e2 100644 --- a/src/alias/id/priority/PriorityEditor.java +++ b/src/alias/id/priority/PriorityEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.priority; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.ActionEvent; @@ -32,17 +34,16 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class PriorityEditor extends ComponentEditor +public class PriorityEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; public static final String DO_NOT_MONITOR = "Do Not Monitor"; private static final String HELP_TEXT = "" - + "

    Call Priority Example

    " + + "

    Call Audio Priority Example

    " + "Priority determines which calls have priority for playback
    " + "over your computer speakers, or designates an alias for
    " + "no-monitoring if you don't want to hear calls from an alias.

    " @@ -55,11 +56,9 @@ public class PriorityEditor extends ComponentEditor public PriorityEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -119,18 +118,18 @@ public class PriorityEditor extends ComponentEditor public Priority getPriority() { - if( getComponent() instanceof Priority ) + if( getItem() instanceof Priority ) { - return (Priority)getComponent(); + return (Priority)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); Priority priority = getPriority(); diff --git a/src/alias/id/priority/PriorityNode.java b/src/alias/id/priority/PriorityNode.java index 2a448d48..52b94cec 100644 --- a/src/alias/id/priority/PriorityNode.java +++ b/src/alias/id/priority/PriorityNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class PriorityNode extends AliasIDNode { diff --git a/src/alias/id/siteID/SiteID.java b/src/alias/id/siteID/SiteID.java index ba5f57e0..a36d2fd5 100644 --- a/src/alias/id/siteID/SiteID.java +++ b/src/alias/id/siteID/SiteID.java @@ -19,8 +19,8 @@ package alias.id.siteID; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class SiteID extends AliasID { diff --git a/src/alias/id/siteID/SiteIDEditor.java b/src/alias/id/siteID/SiteIDEditor.java index 4850422a..b9a1367a 100644 --- a/src/alias/id/siteID/SiteIDEditor.java +++ b/src/alias/id/siteID/SiteIDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.siteID; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -27,10 +29,9 @@ import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class SiteIDEditor extends ComponentEditor +public class SiteIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -47,11 +48,9 @@ public class SiteIDEditor extends ComponentEditor public SiteIDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -82,18 +81,18 @@ public class SiteIDEditor extends ComponentEditor public SiteID getSiteID() { - if( getComponent() instanceof SiteID ) + if( getItem() instanceof SiteID ) { - return (SiteID)getComponent(); + return (SiteID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); SiteID siteID = getSiteID(); diff --git a/src/alias/id/siteID/SiteIDNode.java b/src/alias/id/siteID/SiteIDNode.java index 52cc308b..a234584c 100644 --- a/src/alias/id/siteID/SiteIDNode.java +++ b/src/alias/id/siteID/SiteIDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class SiteIDNode extends AliasIDNode { diff --git a/src/alias/id/status/StatusID.java b/src/alias/id/status/StatusID.java index 5ce9c2ab..b93e0ad8 100644 --- a/src/alias/id/status/StatusID.java +++ b/src/alias/id/status/StatusID.java @@ -19,8 +19,8 @@ package alias.id.status; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class StatusID extends AliasID { diff --git a/src/alias/id/status/StatusIDEditor.java b/src/alias/id/status/StatusIDEditor.java index 6b29e174..e97dd0eb 100644 --- a/src/alias/id/status/StatusIDEditor.java +++ b/src/alias/id/status/StatusIDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.status; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -29,10 +31,9 @@ import javax.swing.JTextField; import javax.swing.text.MaskFormatter; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class StatusIDEditor extends ComponentEditor +public class StatusIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -49,11 +50,9 @@ public class StatusIDEditor extends ComponentEditor public StatusIDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -96,18 +95,18 @@ public class StatusIDEditor extends ComponentEditor public StatusID getStatusID() { - if( getComponent() instanceof StatusID ) + if( getItem() instanceof StatusID ) { - return (StatusID)getComponent(); + return (StatusID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); StatusID statusID = getStatusID(); diff --git a/src/alias/id/status/StatusIDNode.java b/src/alias/id/status/StatusIDNode.java index 19586a5f..2c20df17 100644 --- a/src/alias/id/status/StatusIDNode.java +++ b/src/alias/id/status/StatusIDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class StatusIDNode extends AliasIDNode { diff --git a/src/alias/id/talkgroup/TalkgroupID.java b/src/alias/id/talkgroup/TalkgroupID.java index 2d8da48c..6fea85d8 100644 --- a/src/alias/id/talkgroup/TalkgroupID.java +++ b/src/alias/id/talkgroup/TalkgroupID.java @@ -19,8 +19,8 @@ package alias.id.talkgroup; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; import audio.inverted.AudioType; diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index 981a00c7..f7ab1a96 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.talkgroup; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -27,10 +29,9 @@ import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class TalkgroupIDEditor extends ComponentEditor +public class TalkgroupIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -47,11 +48,9 @@ public class TalkgroupIDEditor extends ComponentEditor public TalkgroupIDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -82,18 +81,18 @@ public class TalkgroupIDEditor extends ComponentEditor public TalkgroupID getTalkgroupID() { - if( getComponent() instanceof TalkgroupID ) + if( getItem() instanceof TalkgroupID ) { - return (TalkgroupID)getComponent(); + return (TalkgroupID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); TalkgroupID talkgroup = getTalkgroupID(); diff --git a/src/alias/id/talkgroup/TalkgroupIDNode.java b/src/alias/id/talkgroup/TalkgroupIDNode.java index 1a11cd08..a58ffb05 100644 --- a/src/alias/id/talkgroup/TalkgroupIDNode.java +++ b/src/alias/id/talkgroup/TalkgroupIDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class TalkgroupIDNode extends AliasIDNode { diff --git a/src/alias/id/uniqueID/UniqueID.java b/src/alias/id/uniqueID/UniqueID.java index f6596294..ab7aedd0 100644 --- a/src/alias/id/uniqueID/UniqueID.java +++ b/src/alias/id/uniqueID/UniqueID.java @@ -19,8 +19,8 @@ package alias.id.uniqueID; import javax.xml.bind.annotation.XmlAttribute; -import alias.AliasID; -import alias.AliasIDType; +import alias.id.AliasID; +import alias.id.AliasIDType; public class UniqueID extends AliasID { diff --git a/src/alias/id/uniqueID/UniqueIDEditor.java b/src/alias/id/uniqueID/UniqueIDEditor.java index 7a90c6aa..8042c54d 100644 --- a/src/alias/id/uniqueID/UniqueIDEditor.java +++ b/src/alias/id/uniqueID/UniqueIDEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package alias.id.uniqueID; +import gui.editor.DocumentListenerEditor; + import java.awt.Color; import java.awt.Cursor; import java.awt.event.MouseAdapter; @@ -27,10 +29,9 @@ import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; -import alias.AliasID; -import alias.ComponentEditor; +import alias.id.AliasID; -public class UniqueIDEditor extends ComponentEditor +public class UniqueIDEditor extends DocumentListenerEditor { private static final long serialVersionUID = 1L; @@ -44,11 +45,9 @@ public class UniqueIDEditor extends ComponentEditor public UniqueIDEditor( AliasID aliasID ) { - super( aliasID ); - initGUI(); - setComponent( aliasID ); + setItem( aliasID ); } private void initGUI() @@ -78,18 +77,18 @@ public class UniqueIDEditor extends ComponentEditor public UniqueID getUniqueID() { - if( getComponent() instanceof UniqueID ) + if( getItem() instanceof UniqueID ) { - return (UniqueID)getComponent(); + return (UniqueID)getItem(); } return null; } @Override - public void setComponent( AliasID aliasID ) + public void setItem( AliasID aliasID ) { - mComponent = aliasID; + super.setItem( aliasID ); UniqueID uid = getUniqueID(); diff --git a/src/alias/id/uniqueID/UniqueIDNode.java b/src/alias/id/uniqueID/UniqueIDNode.java index bdf43be9..430ecdbd 100644 --- a/src/alias/id/uniqueID/UniqueIDNode.java +++ b/src/alias/id/uniqueID/UniqueIDNode.java @@ -26,8 +26,8 @@ import javax.swing.JPanel; import javax.swing.JPopupMenu; import playlist.PlaylistManager; -import alias.AliasIDNode; import alias.AliasNode; +import alias.id.AliasIDNode; public class UniqueIDNode extends AliasIDNode { diff --git a/src/gui/editor/DocumentListenerEditor.java b/src/gui/editor/DocumentListenerEditor.java new file mode 100644 index 00000000..2524de87 --- /dev/null +++ b/src/gui/editor/DocumentListenerEditor.java @@ -0,0 +1,29 @@ +package gui.editor; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public abstract class DocumentListenerEditor extends Editor + implements DocumentListener +{ + private static final long serialVersionUID = 1L; + + public DocumentListenerEditor() + { + } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ) { } +} diff --git a/src/gui/editor/Editor.java b/src/gui/editor/Editor.java new file mode 100644 index 00000000..258cc3bc --- /dev/null +++ b/src/gui/editor/Editor.java @@ -0,0 +1,94 @@ +package gui.editor; + +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.id.AliasIdentifierEditor; + +/** + * Generic editor with change detection, save and reset functions. + */ +public abstract class Editor extends JPanel +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( Editor.class ); + + protected T mItem; + protected boolean mModified = false; + + public Editor() + { + } + + /** + * Sets the object to be edited + */ + public void setItem( T item ) + { + if( isModified() ) + { + int option = JOptionPane.showConfirmDialog( + Editor.this, + "This item has changed. Do you want to save these changes?", + "Save Changes?", + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + save(); + } + + setModified( false ); + } + + mItem = item; + } + + /** + * Current editing item + */ + public T getItem() + { + return mItem; + } + + public boolean hasItem() + { + return mItem != null; + } + + /** + * Sets the contents modified flag + */ + public void setModified( boolean modified ) + { + mModified = modified; + } + + /** + * Indicates if this editor contents have been modified + */ + public boolean isModified() + { + return mModified; + } + + /** + * Save the contents of the editor + */ + public abstract void save(); + + /** + * Reset the contents of the editor without saving any changes + */ + public void reset() + { + mModified = false; + setItem( mItem ); + } +} diff --git a/src/gui/editor/EmptyEditor.java b/src/gui/editor/EmptyEditor.java new file mode 100644 index 00000000..4578df0b --- /dev/null +++ b/src/gui/editor/EmptyEditor.java @@ -0,0 +1,26 @@ +package gui.editor; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; + +public class EmptyEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + public EmptyEditor() + { + setLayout( new MigLayout( "", "[center]", "[grow]" ) ); + add( new JLabel( "Please select an item" ), "wrap" ); + } + + @Override + public void save() + { + } + + @Override + public void reset() + { + } +} diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 5652db05..271d1216 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -599,7 +599,6 @@ public class ProcessingChain implements IChannelEventListener */ public void addMessageListener( Listener listener ) { - mLog.debug( "Registering Message Listener:" + listener.getClass() ); mMessageBroadcaster.addListener( listener ); } From e8fd4f363a7ffce8c94886bb02db3c4d6f9579a1 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 5 Mar 2016 04:30:21 -0500 Subject: [PATCH 44/74] wip: alias multi-select editor feature --- src/alias/AliasController.java | 102 +++++++-- src/alias/AliasEditor.java | 8 +- src/alias/AliasModel.java | 22 +- src/alias/AliasNameEditor.java | 4 +- src/alias/MultipleAliasEditor.java | 329 +++++++++++++++++++++++++++++ src/gui/editor/Editor.java | 1 + 6 files changed, 439 insertions(+), 27 deletions(-) create mode 100644 src/alias/MultipleAliasEditor.java diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index 77fa19c9..cf47664e 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -1,10 +1,15 @@ package alias; +import gui.editor.Editor; + import java.awt.Color; import java.awt.Component; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -38,7 +43,9 @@ public class AliasController extends JPanel private AliasModel mAliasModel; private JTable mAliasTable; private TableFilterHeader mTableFilterHeader; - private AliasEditor mEditor; + private AliasEditor mAliasEditor; + private MultipleAliasEditor mMultipleAliasEditor; + private JideSplitPane mSplitPane; private static final String NEW_ALIAS = "New"; private static final String COPY_ALIAS = "Copy"; @@ -54,13 +61,14 @@ public class AliasController extends JPanel { mAliasModel = aliasModel; - mEditor = new AliasEditor( mAliasModel, settingsManager ); + mAliasEditor = new AliasEditor( mAliasModel, settingsManager ); + mMultipleAliasEditor = new MultipleAliasEditor( mAliasModel, settingsManager ); mIconCellRenderer = new IconCellRenderer( settingsManager ); init(); - mAliasModel.addListener( mEditor ); + mAliasModel.addListener( mAliasEditor ); } private void init() @@ -71,7 +79,7 @@ public class AliasController extends JPanel //System Configuration View and Editor mAliasTable = new JTable( mAliasModel ); - mAliasTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mAliasTable.setSelectionMode( ListSelectionModel.MULTIPLE_INTERVAL_SELECTION ); mAliasTable.getSelectionModel().addListSelectionListener( this ); mAliasTable.setAutoCreateRowSorter( true ); @@ -113,13 +121,52 @@ public class AliasController extends JPanel listAndButtonsPanel.add( tableScroller, "wrap" ); listAndButtonsPanel.add( buttonsPanel ); - JideSplitPane splitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); - splitPane.setDividerSize( 5 ); - splitPane.setShowGripper( true ); - splitPane.add( listAndButtonsPanel ); - splitPane.add( mEditor ); + mSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + mSplitPane.setDividerSize( 5 ); + mSplitPane.setShowGripper( true ); + mSplitPane.add( listAndButtonsPanel ); + mSplitPane.add( mAliasEditor ); - add( splitPane ); + add( mSplitPane ); + } + + /** + * Sets the editor argument as the currently visible alias editor + */ + private void setEditor( Editor editor ) + { + if( mSplitPane.getComponentCount() == 3 ) + { + Component component = mSplitPane.getComponent( 2 ); + + if( component instanceof Editor && component != editor ) + { + //Get current divider location so that we can reapply it after + //changing out the editors, so that the interface doesn't move + int location = mSplitPane.getDividerLocation( 0 ); + + ((Editor)component).setItem( null ); + + mSplitPane.remove( component ); + mSplitPane.add( editor ); + + mSplitPane.validate(); + mSplitPane.setDividerLocation( 0, location ); + mSplitPane.repaint(); + } + } + } + + private Alias getAlias( int selectedRow ) + { + if( selectedRow >= 0 ) + { + int index = mAliasTable.convertRowIndexToModel( mAliasTable.getSelectedRow() ); + + return mAliasModel.getAliasAtIndex( index ); + } + + return null; } @Override @@ -128,26 +175,43 @@ public class AliasController extends JPanel //Limits event firing to only when selection is complete if( !event.getValueIsAdjusting() ) { - int selectedRow = mAliasTable.getSelectedRow(); + int[] selectedRows = mAliasTable.getSelectedRows(); - if( selectedRow != -1 ) + if( selectedRows.length <= 1 ) { - int index = mAliasTable.convertRowIndexToModel( mAliasTable.getSelectedRow() ); + setEditor( mAliasEditor ); - Alias alias = mAliasModel.getAliasAtIndex( index ); + Alias selectedAlias = getAlias( selectedRows[ 0 ] ); - if( alias != null ) + mAliasEditor.setItem( selectedAlias ); + + if( selectedAlias != null ) { - mEditor.setItem( alias ); mCopyButton.setEnabled( true ); mDeleteButton.setEnabled( true ); } + else + { + mCopyButton.setEnabled( false ); + mDeleteButton.setEnabled( false ); + } } else { - mEditor.setItem( null ); - mCopyButton.setEnabled( false ); - mDeleteButton.setEnabled( false ); + setEditor( mMultipleAliasEditor ); + + mCopyButton.setEnabled( true ); + mDeleteButton.setEnabled( true ); + + List selectedAliases = new ArrayList(); + + for( int selectedRow: selectedRows ) + { + selectedAliases.add( getAlias( selectedRow ) ); + } + + mLog.debug( "Selection changed - setting selected aliases" ); + mMultipleAliasEditor.setItem( selectedAliases ); } } } diff --git a/src/alias/AliasEditor.java b/src/alias/AliasEditor.java index 75c176e4..66d8ce5b 100644 --- a/src/alias/AliasEditor.java +++ b/src/alias/AliasEditor.java @@ -34,13 +34,13 @@ import org.slf4j.LoggerFactory; import sample.Listener; import settings.SettingsManager; -import alias.AliasEvent.Event; import alias.action.AliasActionEditor; import alias.id.AliasIdentifierEditor; import com.jidesoft.swing.JideTabbedPane; -public class AliasEditor extends Editor implements ActionListener, Listener +public class AliasEditor extends Editor + implements ActionListener, Listener { private static final long serialVersionUID = 1L; @@ -64,7 +64,7 @@ public class AliasEditor extends Editor implements ActionListener, Listen mAliasModel.addListener( this ); - initGUI(); + init(); } @@ -87,7 +87,7 @@ public class AliasEditor extends Editor implements ActionListener, Listen mAliasActionEditor.setItem( alias ); } - private void initGUI() + private void init() { setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]", "[][][][][][grow,fill]" ) ); diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index f248ded6..d9b3dbfa 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -22,8 +22,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; -import javax.swing.event.TableModelEvent; -import javax.swing.event.TableModelListener; import javax.swing.table.AbstractTableModel; import org.slf4j.Logger; @@ -93,6 +91,26 @@ public class AliasModel extends AbstractTableModel return listNames; } + /** + * Returns a list of alias group names for all aliases + */ + public List getGroupNames() + { + List groupNames = new ArrayList<>(); + + for( Alias alias: mAliases ) + { + if( alias.hasGroup() && !groupNames.contains( alias.getGroup() ) ) + { + groupNames.add( alias.getGroup() ); + } + } + + Collections.sort( groupNames ); + + return groupNames; + } + /** * Returns a list of alias group names for all aliases that have a matching * list name value diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index 3a99e098..10124091 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -284,10 +284,10 @@ public class AliasNameEditor extends Editor alias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); } + setModified( false ); + //Broadcast an alias change event to save the updates mAliasModel.broadcast( new AliasEvent( getItem(), Event.CHANGE ) ); } - - setModified( false ); } } diff --git a/src/alias/MultipleAliasEditor.java b/src/alias/MultipleAliasEditor.java new file mode 100644 index 00000000..94a367ae --- /dev/null +++ b/src/alias/MultipleAliasEditor.java @@ -0,0 +1,329 @@ +package alias; + +import gui.editor.Editor; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; +import java.util.List; + +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSeparator; +import javax.swing.JTextField; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.AliasEvent.Event; +import map.IconManager; +import map.MapIcon; +import map.MapIconListCellRenderer; +import net.miginfocom.swing.MigLayout; +import sample.Listener; +import settings.SettingsManager; + +public class MultipleAliasEditor extends Editor> + implements Listener +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( MultipleAliasEditor.class ); + + private static String HELP_TEXT = + "Select attributes below to change for all selected aliases"; + + private JLabel mAliasCountLabel; + private JLabel mAliasCount; + + private JCheckBox mListCheckBox = new JCheckBox( "List" ); + private JComboBox mListCombo = new JComboBox<>(); + private JCheckBox mGroupCheckBox = new JCheckBox( "Group" ); + private JComboBox mGroupCombo = new JComboBox<>(); + private JCheckBox mIconCheckBox = new JCheckBox( "Icon" ); + private JComboBox mMapIconCombo; + private JCheckBox mColorCheckBox = new JCheckBox( "Color" ); + private JButton mButtonColor; + private JButton mBtnIconManager; + + private AliasModel mAliasModel; + private SettingsManager mSettingsManager; + + public MultipleAliasEditor( AliasModel aliasModel, SettingsManager settingsManager ) + { + mAliasModel = aliasModel; + mAliasModel.addListener( this ); + + mSettingsManager = settingsManager; + + init(); + } + + public List getAliases() + { + if( hasItem() ) + { + return (List)getItem(); + } + + return null; + } + + public void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]", + "[][][][][][][][][][grow,fill]" ) ); + + mAliasCountLabel = new JLabel( "Alias:" ); + add( mAliasCountLabel ); + + mAliasCount = new JLabel( "Multiple" ); + add( mAliasCount, "span" ); + + add( new JSeparator(), "span" ); + + add( new JLabel( HELP_TEXT ), "span" ); + + add( mListCheckBox ); + + mListCombo.setEditable( true ); + mListCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mListCombo, "wrap" ); + + add( mGroupCheckBox ); + + mGroupCombo.setEditable( true ); + mGroupCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mGroupCombo, "wrap" ); + + add( mColorCheckBox ); + + mButtonColor = new JButton( "Select ..." ); + mButtonColor.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Color newColor = JColorChooser.showDialog( + MultipleAliasEditor.this, + "Choose color for this alias", null ); + + if( newColor != null ) + { + mButtonColor.setForeground( newColor ); + mButtonColor.setBackground( newColor ); + + setModified( true ); + } + } + } ); + add( mButtonColor, "wrap" ); + + add( mIconCheckBox ); + + mMapIconCombo = new JComboBox( mSettingsManager.getMapIcons() ); + + MapIconListCellRenderer renderer = new MapIconListCellRenderer(); + renderer.setPreferredSize( new Dimension( 200, 30 ) ); + mMapIconCombo.setRenderer( renderer ); + mMapIconCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( mMapIconCombo, "wrap" ); + + //Dummy place holder + add( new JLabel() ); + + mBtnIconManager = new JButton( "Icon Manager" ); + mBtnIconManager.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + final IconManager iconManager = + new IconManager( mSettingsManager, MultipleAliasEditor.this ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + iconManager.setVisible( true ); + } + } ); + } + } ); + + add( mBtnIconManager, "span,wrap" ); + + JButton saveButton = new JButton( "Save" ); + saveButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + save(); + } + } ); + add( saveButton ); + + JButton resetButton = new JButton( "Reset" ); + resetButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( false ); + setItem( getAliases() ); + } + } ); + add( resetButton ); + + setModified( false ); + } + + @Override + public void setItem( List item ) + { + mLog.debug( "setItem invoked" ); + super.setItem( item ); + + StringBuilder sb = new StringBuilder(); + sb.append( "Multiple [" ); + + if( hasItem() ) + { + sb.append( String.valueOf( getAliases().size() ) ); + + List listNames = mAliasModel.getListNames(); + listNames.add( 0, "" ); + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) ); + + List groupNames = mAliasModel.getGroupNames(); + groupNames.add( 0, "" ); + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) ); + } + else + { + sb.append( "0" ); + } + + sb.append( "]" ); + + mAliasCount.setText( sb.toString() ); + + setModified( false ); + } + + @Override + public void save() + { + if( isModified() && hasItem() ) + { + setModified( false ); + + List aliases = getAliases(); + + for( Alias alias: aliases ) + { + if( mListCheckBox.isSelected() ) + { + String list = null; + + if( mListCombo.getSelectedItem() != null ) + { + list = (String)mListCombo.getSelectedItem(); + } + + alias.setList( list ); + } + + if( mGroupCheckBox.isSelected() ) + { + String group = null; + + if( mGroupCombo.getSelectedItem() != null ) + { + group = (String)mGroupCombo.getSelectedItem(); + } + + mLog.debug( "Group is:" + group ); + alias.setGroup( group ); + } + + if( mColorCheckBox.isSelected() ) + { + alias.setColor( mButtonColor.getForeground().getRGB() ); + } + + if( mIconCheckBox.isSelected() ) + { + if( mMapIconCombo.getSelectedItem() != null ) + { + alias.setIconName( ((MapIcon)mMapIconCombo.getSelectedItem()).getName() ); + } + else + { + alias.setIconName( null ); + } + } + } + + if( mListCheckBox.isSelected() || + mGroupCheckBox.isSelected() || + mColorCheckBox.isSelected() || + mIconCheckBox.isSelected() ) + { + mLog.debug( "Broadcasting change event" ); + + for( Alias alias: aliases ) + { + mLog.debug( "Group:" + alias.getGroup() ); + //Broadcast an alias change event to save the updates + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); + } + } + } + } + + @Override + public void receive( AliasEvent event ) + { + if( event.getEvent() == Event.DELETE && + hasItem() && + getAliases().contains( event.getAlias() ) ) + { + getAliases().remove( event.getAlias() ); + setItem( getAliases() ); + } + } +} diff --git a/src/gui/editor/Editor.java b/src/gui/editor/Editor.java index 258cc3bc..061080c0 100644 --- a/src/gui/editor/Editor.java +++ b/src/gui/editor/Editor.java @@ -31,6 +31,7 @@ public abstract class Editor extends JPanel { if( isModified() ) { + mLog.debug( "Editor that is modified: " + getClass() ); int option = JOptionPane.showConfirmDialog( Editor.this, "This item has changed. Do you want to save these changes?", From 4ad16296d8049f247dc4f5ffc979dae340e444d5 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 5 Mar 2016 07:33:04 -0500 Subject: [PATCH 45/74] wip: alias multi-editor feature complete --- src/alias/Alias.java | 64 +++++- src/alias/AliasController.java | 7 +- src/alias/AliasModel.java | 4 +- src/alias/AliasNode.java | 2 +- src/alias/MultipleAliasEditor.java | 204 +++++++++++++----- .../decode/state/TrafficChannelManager.java | 4 +- src/playlist/PlaylistManager.java | 7 +- 7 files changed, 221 insertions(+), 71 deletions(-) diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 46aef07c..8f88540c 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -29,6 +29,7 @@ import javax.xml.bind.annotation.XmlSeeAlso; import alias.action.AliasAction; import alias.id.AliasID; import alias.id.AliasIDType; +import alias.id.nonrecordable.NonRecordable; import alias.id.priority.Priority; @XmlSeeAlso( { AliasID.class, AliasAction.class } ) @@ -190,7 +191,7 @@ public class Alias return Priority.DEFAULT_PRIORITY; } - public boolean hasPriority() + public boolean hasCallPriority() { for( AliasID id: mAliasIDs ) { @@ -202,6 +203,30 @@ public class Alias return false; } + + /** + * Sets or updates the call priority + */ + public void setCallPriority( int priority ) + { + if( priority == Priority.DO_NOT_MONITOR || + ( Priority.MIN_PRIORITY <= priority && priority <= Priority.MAX_PRIORITY ) ) + { + for( AliasID id: mAliasIDs ) + { + if( id.getType() == AliasIDType.Priority ) + { + ((Priority)id).setPriority( priority ); + return; + } + } + + //If we don't find a priority id, create one + Priority p = new Priority(); + p.setPriority( priority ); + addAliasID( p ); + } + } /** * Inspects the alias for a non-recordable alias id. Default is true; @@ -218,4 +243,41 @@ public class Alias return true; } + + /** + * Sets or removes the non-recordable alias ID for this alias. + */ + public void setRecordable( boolean recordable ) + { + if( recordable ) + { + AliasID toRemove = null; + + for( AliasID id: getId() ) + { + if( id.getType() == AliasIDType.NonRecordable ) + { + toRemove = id; + break; + } + } + + if( toRemove != null ) + { + removeAliasID( toRemove ); + } + } + else + { + for( AliasID id: getId() ) + { + if( id.getType() == AliasIDType.NonRecordable ) + { + return; + } + } + + addAliasID( new NonRecordable() ); + } + } } diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index cf47664e..5b8984a8 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -161,7 +161,7 @@ public class AliasController extends JPanel { if( selectedRow >= 0 ) { - int index = mAliasTable.convertRowIndexToModel( mAliasTable.getSelectedRow() ); + int index = mAliasTable.convertRowIndexToModel( selectedRow ); return mAliasModel.getAliasAtIndex( index ); } @@ -207,10 +207,11 @@ public class AliasController extends JPanel for( int selectedRow: selectedRows ) { - selectedAliases.add( getAlias( selectedRow ) ); + Alias alias = getAlias( selectedRow ); + + selectedAliases.add( alias ); } - mLog.debug( "Selection changed - setting selected aliases" ); mMultipleAliasEditor.setItem( selectedAliases ); } } diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index d9b3dbfa..9e346269 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -266,14 +266,14 @@ public class AliasModel extends AbstractTableModel public void broadcast( AliasEvent event ) { - mAliasEventBroadcaster.broadcast( event ); - if( event.getEvent() == Event.CHANGE ) { int index = mAliases.indexOf( event.getAlias() ); fireTableRowsUpdated( index, index ); } + + mAliasEventBroadcaster.broadcast( event ); } public void addListener( Listener listener ) diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java index 634c5665..2c5ec25b 100644 --- a/src/alias/AliasNode.java +++ b/src/alias/AliasNode.java @@ -515,7 +515,7 @@ public class AliasNode extends ConfigurableNode retVal.addSeparator(); } - if( !getAlias().hasPriority() ) + if( !getAlias().hasCallPriority() ) { JMenuItem priorityItem = new JMenuItem( "Set Call Priority" ); diff --git a/src/alias/MultipleAliasEditor.java b/src/alias/MultipleAliasEditor.java index 94a367ae..0bf1f871 100644 --- a/src/alias/MultipleAliasEditor.java +++ b/src/alias/MultipleAliasEditor.java @@ -18,12 +18,16 @@ import javax.swing.JColorChooser; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JSeparator; +import javax.swing.JSlider; import javax.swing.JTextField; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import alias.AliasEvent.Event; +import alias.id.priority.Priority; import map.IconManager; import map.MapIcon; import map.MapIconListCellRenderer; @@ -52,6 +56,11 @@ public class MultipleAliasEditor extends Editor> private JCheckBox mColorCheckBox = new JCheckBox( "Color" ); private JButton mButtonColor; private JButton mBtnIconManager; + private JCheckBox mRecordCheckBox = new JCheckBox( "Record" ); + private JComboBox mRecordActionCombo = new JComboBox<>( Record.values() ); + private JCheckBox mPriorityCheckBox = new JCheckBox( "Priority" ); + private JSlider mPrioritySlider; + private JLabel mPrioritySliderLabel; private AliasModel mAliasModel; private SettingsManager mSettingsManager; @@ -79,7 +88,7 @@ public class MultipleAliasEditor extends Editor> public void init() { setLayout( new MigLayout( "fill,wrap 2", "[grow,fill][grow,fill]", - "[][][][][][][][][][grow,fill]" ) ); + "[][][][][][][][][][][][][grow,fill]" ) ); mAliasCountLabel = new JLabel( "Alias:" ); add( mAliasCountLabel ); @@ -94,27 +103,11 @@ public class MultipleAliasEditor extends Editor> add( mListCheckBox ); mListCombo.setEditable( true ); - mListCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); add( mListCombo, "wrap" ); add( mGroupCheckBox ); mGroupCombo.setEditable( true ); - mGroupCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); add( mGroupCombo, "wrap" ); add( mColorCheckBox ); @@ -133,8 +126,6 @@ public class MultipleAliasEditor extends Editor> { mButtonColor.setForeground( newColor ); mButtonColor.setBackground( newColor ); - - setModified( true ); } } } ); @@ -147,15 +138,6 @@ public class MultipleAliasEditor extends Editor> MapIconListCellRenderer renderer = new MapIconListCellRenderer(); renderer.setPreferredSize( new Dimension( 200, 30 ) ); mMapIconCombo.setRenderer( renderer ); - mMapIconCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - add( mMapIconCombo, "wrap" ); //Dummy place holder @@ -183,6 +165,47 @@ public class MultipleAliasEditor extends Editor> add( mBtnIconManager, "span,wrap" ); + add( mRecordCheckBox ); + add( mRecordActionCombo ); + + //Placeholder + add( new JLabel( " " ) ); + mPrioritySliderLabel = new JLabel( "Priority: " + Priority.MIN_PRIORITY ); + add( mPrioritySliderLabel ); + + add( mPriorityCheckBox ); + + mPrioritySlider = new JSlider( JSlider.HORIZONTAL, + Priority.MIN_PRIORITY, + Priority.MAX_PRIORITY + 1, + Priority.MIN_PRIORITY ); + + mPrioritySlider.setMajorTickSpacing( 20 ); + mPrioritySlider.setMinorTickSpacing( 5 ); + mPrioritySlider.setPaintTicks( true ); + mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); + mPrioritySlider.setPaintLabels( true ); + mPrioritySlider.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + int priority = mPrioritySlider.getValue(); + + if( priority > Priority.MAX_PRIORITY ) + { + mPrioritySliderLabel.setText( "Priority: Do Not Monitor" ); + } + else + { + mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); + } + } + } ); + mPrioritySlider.setToolTipText( HELP_TEXT ); + + add( mPrioritySlider ); + JButton saveButton = new JButton( "Save" ); saveButton.addActionListener( new ActionListener() { @@ -200,8 +223,7 @@ public class MultipleAliasEditor extends Editor> @Override public void actionPerformed( ActionEvent e ) { - setModified( false ); - setItem( getAliases() ); + reset(); } } ); add( resetButton ); @@ -212,45 +234,79 @@ public class MultipleAliasEditor extends Editor> @Override public void setItem( List item ) { - mLog.debug( "setItem invoked" ); super.setItem( item ); StringBuilder sb = new StringBuilder(); - sb.append( "Multiple [" ); + sb.append( "Multiple Selected [ " ); if( hasItem() ) { sb.append( String.valueOf( getAliases().size() ) ); - - List listNames = mAliasModel.getListNames(); - listNames.add( 0, "" ); - mListCombo.setModel( new DefaultComboBoxModel( - listNames.toArray( new String[ listNames.size() ] ) ) ); - - List groupNames = mAliasModel.getGroupNames(); - groupNames.add( 0, "" ); - mGroupCombo.setModel( new DefaultComboBoxModel( - groupNames.toArray( new String[ groupNames.size() ] ) ) ); } else { sb.append( "0" ); } - sb.append( "]" ); + sb.append( " ]" ); mAliasCount.setText( sb.toString() ); + List listNames = mAliasModel.getListNames(); + listNames.add( 0, "" ); + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) ); + + List groupNames = mAliasModel.getGroupNames(); + groupNames.add( 0, "" ); + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) ); + setModified( false ); } + @Override + public void reset() + { + //Reset multi-editor controls + mListCheckBox.setSelected( false ); + mGroupCheckBox.setSelected( false ); + mColorCheckBox.setSelected( false ); + mIconCheckBox.setSelected( false ); + mRecordCheckBox.setSelected( false ); + mPriorityCheckBox.setSelected( false ); + + List listNames = mAliasModel.getListNames(); + listNames.add( 0, "" ); + mListCombo.setModel( new DefaultComboBoxModel( + listNames.toArray( new String[ listNames.size() ] ) ) ); + + List groupNames = mAliasModel.getGroupNames(); + groupNames.add( 0, "" ); + mGroupCombo.setModel( new DefaultComboBoxModel( + groupNames.toArray( new String[ groupNames.size() ] ) ) ); + + mButtonColor.setForeground( getForeground() ); + mButtonColor.setBackground( getBackground() ); + + super.reset(); + } + + private boolean hasRequestedChanges() + { + return mListCheckBox.isSelected() || + mGroupCheckBox.isSelected() || + mColorCheckBox.isSelected() || + mIconCheckBox.isSelected() || + mRecordCheckBox.isSelected() || + mPriorityCheckBox.isSelected(); + } + @Override public void save() { - if( isModified() && hasItem() ) + if( hasRequestedChanges() && hasItem() ) { - setModified( false ); - List aliases = getAliases(); for( Alias alias: aliases ) @@ -276,7 +332,6 @@ public class MultipleAliasEditor extends Editor> group = (String)mGroupCombo.getSelectedItem(); } - mLog.debug( "Group is:" + group ); alias.setGroup( group ); } @@ -296,21 +351,34 @@ public class MultipleAliasEditor extends Editor> alias.setIconName( null ); } } + + if( mRecordCheckBox.isSelected() ) + { + Record action = + (Record)mRecordActionCombo.getSelectedItem(); + + alias.setRecordable( action == Record.RECORDABLE ); + } + + if( mPriorityCheckBox.isSelected() ) + { + int priority = mPrioritySlider.getValue(); + + //This is a work-around -- we use max priority + 1 in the + //gui to indicate do not monitor, and change the value here + if( priority == Priority.MAX_PRIORITY + 1 ) + { + priority = Priority.DO_NOT_MONITOR; + } + + alias.setCallPriority( priority ); + } } - if( mListCheckBox.isSelected() || - mGroupCheckBox.isSelected() || - mColorCheckBox.isSelected() || - mIconCheckBox.isSelected() ) + for( Alias alias: aliases ) { - mLog.debug( "Broadcasting change event" ); - - for( Alias alias: aliases ) - { - mLog.debug( "Group:" + alias.getGroup() ); - //Broadcast an alias change event to save the updates - mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); - } + //Broadcast an alias change event to save the updates + mAliasModel.broadcast( new AliasEvent( alias, Event.CHANGE ) ); } } } @@ -326,4 +394,22 @@ public class MultipleAliasEditor extends Editor> setItem( getAliases() ); } } + + public enum Record + { + NON_RECORDABLE( "Non-Recordable" ), + RECORDABLE( "Recordable" ); + + private String mLabel; + + private Record( String label ) + { + mLabel = label; + } + + public String toString() + { + return mLabel; + } + } } diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index ff4eae65..b78b9ea8 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -317,7 +317,7 @@ public class TrafficChannelManager extends Module Alias to = event.getToIDAlias(); if( to != null && - to.hasPriority() && + to.hasCallPriority() && to.getCallPriority() == Priority.DO_NOT_MONITOR ) { return true; @@ -326,7 +326,7 @@ public class TrafficChannelManager extends Module Alias from = event.getFromIDAlias(); if( from != null && - from.hasPriority() && + from.hasCallPriority() && from.getCallPriority() == Priority.DO_NOT_MONITOR ) { return true; diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index 64b21309..86e447a7 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -504,16 +504,17 @@ public class PlaylistManager implements ChannelEventListener, Listener Date: Sat, 5 Mar 2016 08:23:08 -0500 Subject: [PATCH 46/74] wip: adds call priority and non-recordable editors to the alias multi-select editor. Changes example hyperlink to 'Help'. Ensures editor only allows 1 instance of call priority or non-recordable can be added to any alias. --- src/alias/Alias.java | 3 + src/alias/action/AliasActionEditor.java | 11 ++- src/alias/action/beep/BeepActionEditor.java | 10 +-- src/alias/action/clip/ClipActionEditor.java | 10 +-- .../action/script/ScriptActionEditor.java | 10 +-- src/alias/id/AliasIdentifierEditor.java | 43 ++++++++++-- src/alias/id/esn/ESNEditor.java | 14 ++-- src/alias/id/fleetsync/FleetsyncIDEditor.java | 14 ++-- src/alias/id/lojack/LoJackIDEditor.java | 14 ++-- src/alias/id/mdc/MDC1200IDEditor.java | 14 ++-- src/alias/id/mobileID/MINEditor.java | 14 ++-- src/alias/id/mpt1327/MPT1327IDEditor.java | 14 ++-- .../id/nonrecordable/NonRecordableEditor.java | 21 +++--- src/alias/id/priority/PriorityEditor.java | 69 ++++++++++--------- src/alias/id/siteID/SiteIDEditor.java | 14 ++-- src/alias/id/status/StatusIDEditor.java | 14 ++-- src/alias/id/talkgroup/TalkgroupIDEditor.java | 14 ++-- src/alias/id/uniqueID/UniqueIDEditor.java | 14 ++-- 18 files changed, 184 insertions(+), 133 deletions(-) diff --git a/src/alias/Alias.java b/src/alias/Alias.java index 8f88540c..b17a92b9 100644 --- a/src/alias/Alias.java +++ b/src/alias/Alias.java @@ -25,6 +25,7 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; import alias.action.AliasAction; import alias.id.AliasID; @@ -178,6 +179,7 @@ public class Alias /** * Returns the priority level of this alias, if defined, or the default priority */ + @XmlTransient public int getCallPriority() { for( AliasID id: mAliasIDs ) @@ -231,6 +233,7 @@ public class Alias /** * Inspects the alias for a non-recordable alias id. Default is true; */ + @XmlTransient public boolean isRecordable() { for( AliasID id: getId() ) diff --git a/src/alias/action/AliasActionEditor.java b/src/alias/action/AliasActionEditor.java index 96b0f9a9..c6307fc0 100644 --- a/src/alias/action/AliasActionEditor.java +++ b/src/alias/action/AliasActionEditor.java @@ -228,9 +228,14 @@ public class AliasActionEditor extends Editor @Override public void save() { - if( mEditorContainer.isModified() ) + if( isModified() || mEditorContainer.isModified() ) { - mEditorContainer.save(); + if( mEditorContainer.isModified() ) + { + mEditorContainer.save(); + } + + setModified( false ); if( hasItem() ) { @@ -316,6 +321,8 @@ public class AliasActionEditor extends Editor setItem( alias ); mAliasActionList.setSelectedValue( action, true ); + + AliasActionEditor.this.setModified( true ); } } ); } diff --git a/src/alias/action/beep/BeepActionEditor.java b/src/alias/action/beep/BeepActionEditor.java index 139f01e0..ccf74e7e 100644 --- a/src/alias/action/beep/BeepActionEditor.java +++ b/src/alias/action/beep/BeepActionEditor.java @@ -141,10 +141,10 @@ public class BeepActionEditor extends DocumentListenerEditor add( mPeriodSliderLabel ); add( mPeriodSlider, "wrap,grow" ); - JLabel example = new JLabel( "Help ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) @@ -153,7 +153,7 @@ public class BeepActionEditor extends DocumentListenerEditor HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } @Override diff --git a/src/alias/action/clip/ClipActionEditor.java b/src/alias/action/clip/ClipActionEditor.java index 070e0ce9..6764a8d9 100644 --- a/src/alias/action/clip/ClipActionEditor.java +++ b/src/alias/action/clip/ClipActionEditor.java @@ -232,10 +232,10 @@ public class ClipActionEditor extends DocumentListenerEditor add( testButton, "grow,wrap" ); - JLabel example = new JLabel( "Help ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) @@ -244,7 +244,7 @@ public class ClipActionEditor extends DocumentListenerEditor HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } @Override diff --git a/src/alias/action/script/ScriptActionEditor.java b/src/alias/action/script/ScriptActionEditor.java index ea7e181e..b89d50f7 100644 --- a/src/alias/action/script/ScriptActionEditor.java +++ b/src/alias/action/script/ScriptActionEditor.java @@ -229,10 +229,10 @@ public class ScriptActionEditor extends DocumentListenerEditor add( testButton, "grow,wrap" ); - JLabel example = new JLabel( "Help ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) @@ -241,7 +241,7 @@ public class ScriptActionEditor extends DocumentListenerEditor HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } @Override diff --git a/src/alias/id/AliasIdentifierEditor.java b/src/alias/id/AliasIdentifierEditor.java index f2b89a48..1e72467f 100644 --- a/src/alias/id/AliasIdentifierEditor.java +++ b/src/alias/id/AliasIdentifierEditor.java @@ -135,9 +135,35 @@ public class AliasIdentifierEditor extends Editor menu.add( new AddAliasIdentifierItem( AliasIDType.Site ) ); menu.add( new AddAliasIdentifierItem( AliasIDType.Status ) ); menu.add( new AddAliasIdentifierItem( AliasIDType.Talkgroup ) ); - menu.addSeparator(); - menu.add( new AddAliasIdentifierItem( AliasIDType.Priority ) ); - menu.add( new AddAliasIdentifierItem( AliasIDType.NonRecordable ) ); + + boolean separatorAdded = false; + + if( hasItem() ) + { + Alias alias = getItem(); + + if( !alias.hasCallPriority() ) + { + if( !separatorAdded ) + { + menu.addSeparator(); + separatorAdded = true; + } + + menu.add( new AddAliasIdentifierItem( AliasIDType.Priority ) ); + } + + if( alias.isRecordable() ) + { + if( !separatorAdded ) + { + menu.addSeparator(); + separatorAdded = true; + } + + menu.add( new AddAliasIdentifierItem( AliasIDType.NonRecordable ) ); + } + } menu.show( e.getComponent(), e.getX(), e.getY() ); } @@ -238,9 +264,14 @@ public class AliasIdentifierEditor extends Editor @Override public void save() { - if( mEditorContainer.isModified() ) + if( isModified() || mEditorContainer.isModified() ) { - mEditorContainer.save(); + if( mEditorContainer.isModified() ) + { + mEditorContainer.save(); + } + + setModified( false ); if( hasItem() ) { @@ -326,6 +357,8 @@ public class AliasIdentifierEditor extends Editor setItem( alias ); mAliasIDList.setSelectedValue( id, true ); + + AliasIdentifierEditor.this.setModified( true ); } } ); } diff --git a/src/alias/id/esn/ESNEditor.java b/src/alias/id/esn/ESNEditor.java index 5973c291..fd36ed47 100644 --- a/src/alias/id/esn/ESNEditor.java +++ b/src/alias/id/esn/ESNEditor.java @@ -36,7 +36,7 @@ public class ESNEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = - "

    Electronic Serial Number (ESN) Example

    " + "

    Electronic Serial Number (ESN)

    " + "ESN: hexadecimal 0-9, A-F (e.g. ABCD1234 )
    " + "Wildcard: use an asterisk (*) to wildcard individual
    " + "digits (e.g. ABCD123* or AB**1**4)" @@ -61,19 +61,19 @@ public class ESNEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( ESNEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public Esn getEsn() diff --git a/src/alias/id/fleetsync/FleetsyncIDEditor.java b/src/alias/id/fleetsync/FleetsyncIDEditor.java index b4dbc7ba..64f8f531 100644 --- a/src/alias/id/fleetsync/FleetsyncIDEditor.java +++ b/src/alias/id/fleetsync/FleetsyncIDEditor.java @@ -44,7 +44,7 @@ public class FleetsyncIDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    Fleetsync Identifier Example

    " + + "

    Fleetsync Identifier

    " + "Fleetsync: ggg-uuuu where g=Group and u=Unit (e.g. 001-0001)
    " + "Wildcard: use an asterisk (*) for each digit (e.g. 001-****)" + ""; @@ -82,19 +82,19 @@ public class FleetsyncIDEditor extends DocumentListenerEditor add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( FleetsyncIDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public FleetsyncID getFleetsyncID() diff --git a/src/alias/id/lojack/LoJackIDEditor.java b/src/alias/id/lojack/LoJackIDEditor.java index aa6ae805..8c13e60e 100644 --- a/src/alias/id/lojack/LoJackIDEditor.java +++ b/src/alias/id/lojack/LoJackIDEditor.java @@ -47,7 +47,7 @@ public class LoJackIDEditor extends DocumentListenerEditor private JComboBox mFunctionCombo; private static final String HELP_TEXT = "" - + "

    LoJack Example

    " + + "

    LoJack Function and Identifier

    " + "Function Code: 1Y-SITE ID
    " + "ID: 5 numbers or characters (e.g. 1BN47)
    " + "
    " @@ -103,19 +103,19 @@ public class LoJackIDEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( LoJackIDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public LoJackFunctionAndID getLoJackID() diff --git a/src/alias/id/mdc/MDC1200IDEditor.java b/src/alias/id/mdc/MDC1200IDEditor.java index eb092e35..21437cb9 100644 --- a/src/alias/id/mdc/MDC1200IDEditor.java +++ b/src/alias/id/mdc/MDC1200IDEditor.java @@ -38,7 +38,7 @@ public class MDC1200IDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    MDC-1200 Identifier Example

    " + + "

    MDC-1200 Identifier

    " + "MDC-1200: four digit [0-9] value (e.g. 1234)
    " + "Wildcard: use an asterisk (*) to wildcard individual
    " + "digits (e.g. 123* or **34)" @@ -76,19 +76,19 @@ public class MDC1200IDEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( MDC1200IDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public MDC1200ID getMDC1200ID() diff --git a/src/alias/id/mobileID/MINEditor.java b/src/alias/id/mobileID/MINEditor.java index f873e70e..6244ff69 100644 --- a/src/alias/id/mobileID/MINEditor.java +++ b/src/alias/id/mobileID/MINEditor.java @@ -38,7 +38,7 @@ public class MINEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    Passport Mobile ID (MIN) Example

    " + + "

    Passport Mobile ID (MIN)

    " + "MIN: six character hex value (e.g. AB12CD)
    " + "Wildcard: use an asterisk (*) to wildcard individual
    " + "digits (e.g. AB**CD or AB12**)" @@ -76,19 +76,19 @@ public class MINEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( MINEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public Min getMin() diff --git a/src/alias/id/mpt1327/MPT1327IDEditor.java b/src/alias/id/mpt1327/MPT1327IDEditor.java index 1e9283c8..6c487814 100644 --- a/src/alias/id/mpt1327/MPT1327IDEditor.java +++ b/src/alias/id/mpt1327/MPT1327IDEditor.java @@ -38,7 +38,7 @@ public class MPT1327IDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    MPT-1327 Identifier Example

    " + + "

    MPT-1327 Identifier

    " + "MPT-1327: decimal (0-9) format ppp-iiii where
    " + "p=Prefix and i=Ident (e.g. 123-0001)
    " + "Wildcard: use an asterisk (*) to wildcard individual
    " @@ -78,19 +78,19 @@ public class MPT1327IDEditor extends DocumentListenerEditor add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( MPT1327IDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public MPT1327ID getMPT1327ID() diff --git a/src/alias/id/nonrecordable/NonRecordableEditor.java b/src/alias/id/nonrecordable/NonRecordableEditor.java index 35578a51..6f402371 100644 --- a/src/alias/id/nonrecordable/NonRecordableEditor.java +++ b/src/alias/id/nonrecordable/NonRecordableEditor.java @@ -35,10 +35,13 @@ public class NonRecordableEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    Audio Non-Recordable Example

    " + + "

    Audio Non-Recordable

    " + "This identifies an alias as non-recordable.

    " - + "The alias can still be monitored but associated audio will not be recorded
    " - + "when recording is enabled for the decoding channel."; + + "The alias can still be monitored but associated audio will
    " + + "not be recorded.

    " + + "The default behavior when audio recording is enabled for a
    " + + "channel is to record all aliases except any alias with a
    " + + "non-recordable identifier"; public NonRecordableEditor( AliasID aliasID ) { @@ -53,19 +56,19 @@ public class NonRecordableEditor extends DocumentListenerEditor add( new JLabel( "Non-Recordable" ), "wrap" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( NonRecordableEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } @Override diff --git a/src/alias/id/priority/PriorityEditor.java b/src/alias/id/priority/PriorityEditor.java index db3868e2..9b62ff3f 100644 --- a/src/alias/id/priority/PriorityEditor.java +++ b/src/alias/id/priority/PriorityEditor.java @@ -43,14 +43,14 @@ public class PriorityEditor extends DocumentListenerEditor public static final String DO_NOT_MONITOR = "Do Not Monitor"; private static final String HELP_TEXT = "" - + "

    Call Audio Priority Example

    " + + "

    Call Audio Priority

    " + "Priority determines which calls have priority for playback
    " + "over your computer speakers, or designates an alias for
    " + "no-monitoring if you don't want to hear calls from an alias.

    " - + "Lower values indicate higher priority levels." + + "Lower values indicate higher priority levels.

    " + + "Do Not Monitor: slide priority all the way to the right" + ""; - private JCheckBox mDoNotMonitorCheckBox = new JCheckBox( DO_NOT_MONITOR ); private JSlider mPrioritySlider; private JLabel mPrioritySliderLabel; @@ -65,20 +65,9 @@ public class PriorityEditor extends DocumentListenerEditor { setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][]" ) ); - mDoNotMonitorCheckBox.setToolTipText( HELP_TEXT ); - mDoNotMonitorCheckBox.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - setModified( true ); - } - } ); - add( mDoNotMonitorCheckBox, "span,align center" ); - mPrioritySlider = new JSlider( JSlider.HORIZONTAL, Priority.MIN_PRIORITY, - Priority.MAX_PRIORITY, + Priority.MAX_PRIORITY + 1, Priority.MIN_PRIORITY ); mPrioritySlider.setMajorTickSpacing( 20 ); @@ -91,29 +80,39 @@ public class PriorityEditor extends DocumentListenerEditor @Override public void stateChanged( ChangeEvent e ) { - mPrioritySliderLabel.setText( "Priority: " + mPrioritySlider.getValue() ); + int priority = mPrioritySlider.getValue(); + + if( priority == Priority.MAX_PRIORITY + 1 ) + { + mPrioritySliderLabel.setText( "Priority: Do Not Monitor" ); + } + else + { + mPrioritySliderLabel.setText( "Priority: " + priority ); + } + setModified( true ); } } ); mPrioritySlider.setToolTipText( HELP_TEXT ); mPrioritySliderLabel = new JLabel( "Priority: " + mPrioritySlider.getValue() + " " ); - add( mPrioritySliderLabel ); - add( mPrioritySlider, "wrap,grow" ); + add( mPrioritySliderLabel, "span,align center" ); + add( mPrioritySlider, "span,grow" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( PriorityEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public Priority getPriority() @@ -135,8 +134,14 @@ public class PriorityEditor extends DocumentListenerEditor if( priority != null ) { - mPrioritySlider.setValue( priority.getPriority() ); - mDoNotMonitorCheckBox.setSelected( priority.isDoNotMonitor() ); + int value = priority.getPriority(); + + if( value == Priority.DO_NOT_MONITOR ) + { + value = Priority.MAX_PRIORITY + 1; + } + + mPrioritySlider.setValue( value ); } setModified( false ); @@ -151,14 +156,14 @@ public class PriorityEditor extends DocumentListenerEditor if( priority != null ) { - if( mDoNotMonitorCheckBox.isSelected() ) + int value = mPrioritySlider.getValue(); + + if( value == Priority.MAX_PRIORITY + 1 ) { - priority.setPriority( Priority.DO_NOT_MONITOR ); - } - else - { - priority.setPriority( mPrioritySlider.getValue() ); + value = Priority.DO_NOT_MONITOR; } + + priority.setPriority( value ); } setModified( false ); diff --git a/src/alias/id/siteID/SiteIDEditor.java b/src/alias/id/siteID/SiteIDEditor.java index b9a1367a..7df5fa7a 100644 --- a/src/alias/id/siteID/SiteIDEditor.java +++ b/src/alias/id/siteID/SiteIDEditor.java @@ -36,7 +36,7 @@ public class SiteIDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    Site ID Example

    " + + "

    Site Identifier

    " + "LTR-Net: decimal (0-9) (e.g. 019)
    " + "MPT-1327: 5 digits (0-9) (e.g. 23619)
    " + "Passport: 3 digits (0-9) (e.g. 019)
    " @@ -63,20 +63,20 @@ public class SiteIDEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( SiteIDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public SiteID getSiteID() diff --git a/src/alias/id/status/StatusIDEditor.java b/src/alias/id/status/StatusIDEditor.java index e97dd0eb..e4dc8e3d 100644 --- a/src/alias/id/status/StatusIDEditor.java +++ b/src/alias/id/status/StatusIDEditor.java @@ -38,7 +38,7 @@ public class StatusIDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    Status Example

    " + + "

    Status Identifier

    " + "Status numbers are used in some protocols like Fleetsync.

    " + "The status number is assigned a meaning in the radio. You
    " + "can assign a 3 digit status code (use leading zeros) to an
    " @@ -78,19 +78,19 @@ public class StatusIDEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( StatusIDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public StatusID getStatusID() diff --git a/src/alias/id/talkgroup/TalkgroupIDEditor.java b/src/alias/id/talkgroup/TalkgroupIDEditor.java index f7ab1a96..6a63faf6 100644 --- a/src/alias/id/talkgroup/TalkgroupIDEditor.java +++ b/src/alias/id/talkgroup/TalkgroupIDEditor.java @@ -36,7 +36,7 @@ public class TalkgroupIDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = - "

    Talkgroup Identifier Examples

    " + "

    Talkgroup Identifier

    " + "P25: 4 or 6 hex characters (e.g. AB12 or ABC123)
    " + "LTR: A-HH-TTT where A=Area H=Home T=Talkgroup (0-01-128)
    " + "Passport: 5-digit number (12345 or 00023)
    " @@ -64,19 +64,19 @@ public class TalkgroupIDEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( TalkgroupIDEditor.this, - HELP_TEXT, "Examples", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public TalkgroupID getTalkgroupID() diff --git a/src/alias/id/uniqueID/UniqueIDEditor.java b/src/alias/id/uniqueID/UniqueIDEditor.java index 8042c54d..ea0aa3be 100644 --- a/src/alias/id/uniqueID/UniqueIDEditor.java +++ b/src/alias/id/uniqueID/UniqueIDEditor.java @@ -36,7 +36,7 @@ public class UniqueIDEditor extends DocumentListenerEditor private static final long serialVersionUID = 1L; private static final String HELP_TEXT = "" - + "

    LTR-Net Unique ID (UID) Example

    " + + "

    LTR-Net Unique ID (UID)

    " + "UID: identifier assigned to each radio in
    " + "the range 1 - 2097152" + ""; @@ -60,19 +60,19 @@ public class UniqueIDEditor extends DocumentListenerEditor mTextField.setToolTipText( HELP_TEXT ); add( mTextField, "growx,push" ); - JLabel example = new JLabel( "Example ..." ); - example.setForeground( Color.BLUE.brighter() ); - example.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); - example.addMouseListener( new MouseAdapter() + JLabel help = new JLabel( "Help ..." ); + help.setForeground( Color.BLUE.brighter() ); + help.setCursor( new Cursor( Cursor.HAND_CURSOR ) ); + help.addMouseListener( new MouseAdapter() { @Override public void mouseClicked( MouseEvent e ) { JOptionPane.showMessageDialog( UniqueIDEditor.this, - HELP_TEXT, "Example", JOptionPane.INFORMATION_MESSAGE ); + HELP_TEXT, "Help", JOptionPane.INFORMATION_MESSAGE ); } } ); - add( example ); + add( help, "align left" ); } public UniqueID getUniqueID() From 8aebdd1c26396fc5464c27d469e8cf4608b00662 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 5 Mar 2016 08:25:22 -0500 Subject: [PATCH 47/74] Changes default channel source to be a tuner. --- src/source/config/SourceConfigFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/source/config/SourceConfigFactory.java b/src/source/config/SourceConfigFactory.java index f4d02f39..6c271cb2 100644 --- a/src/source/config/SourceConfigFactory.java +++ b/src/source/config/SourceConfigFactory.java @@ -23,7 +23,7 @@ public class SourceConfigFactory { public static SourceConfiguration getDefaultSourceConfiguration() { - return getSourceConfiguration( SourceType.NONE ); + return getSourceConfiguration( SourceType.TUNER ); } public static SourceConfiguration From 2394ac2fa91a5a241854337108e9441f203008a1 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 6 Mar 2016 07:48:14 -0500 Subject: [PATCH 48/74] WIP #34: added channel map manager and new editor window. Removed a number of unused classes. Removed playlist manager from a number of classes and replaced with the alias, channel and channel map models. Updated playlist manager to no longer retain a playlist except for when loading to models or when storing from models. Extracted version 1 to version 2 playlist conversion and changed playlist manager to keep the original version 1 playlist and create a new version 2 (playlist_v2.xml) file. On startup, try to load a v2, and if not, look for a v1 to convert. Otherwise, don't create a playlist and one will be created once the user adds items to the models. --- src/alias/AliasDirectoryNode.java | 103 --- src/alias/AliasList.java | 634 +++++++++++------- src/alias/AliasListNode.java | 136 ---- src/alias/AliasModel.java | 28 + src/alias/AliasNode.java | 572 ---------------- src/alias/GroupEditor.java | 110 --- src/alias/GroupNode.java | 135 ---- src/alias/ListEditor.java | 107 --- src/alias/action/AliasActionNode.java | 45 -- src/alias/action/beep/BeepActionNode.java | 89 --- src/alias/action/clip/ClipActionNode.java | 89 --- src/alias/action/script/ScriptActionNode.java | 89 --- src/alias/id/AliasID.java | 2 +- src/alias/id/esn/ESNNode.java | 87 --- src/alias/id/fleetsync/FleetsyncIDNode.java | 98 --- src/alias/id/lojack/LoJackIDNode.java | 87 --- src/alias/id/mdc/MDC1200IDNode.java | 92 --- src/alias/id/mobileID/MINNode.java | 87 --- src/alias/id/mpt1327/MPT1327IDNode.java | 92 --- .../id/nonrecordable/NonRecordableNode.java | 89 --- src/alias/id/priority/PriorityNode.java | 87 --- src/alias/id/siteID/SiteIDNode.java | 87 --- src/alias/id/status/StatusIDNode.java | 87 --- src/alias/id/talkgroup/TalkgroupIDNode.java | 87 --- src/alias/id/uniqueID/UniqueIDNode.java | 87 --- .../ConfigurationControllerModel.java | 51 +- src/controller/ControllerPanel.java | 9 +- src/controller/channel/ChannelController.java | 13 +- src/controller/channel/ChannelEditor.java | 17 +- src/controller/channel/ChannelNode.java | 190 ------ .../channel/ChannelProcessingManager.java | 13 +- src/controller/channel/ChannelUtils.java | 14 +- .../channel/NameConfigurationEditor.java | 87 ++- src/controller/channel/OldChannelEditor.java | 260 ------- .../channel/map/ChannelMapEditor.java | 200 +++--- .../channel/map/ChannelMapEvent.java | 76 +++ .../channel/map/ChannelMapListNode.java | 88 --- .../channel/map/ChannelMapManager.java | 47 ++ .../channel/map/ChannelMapManagerFrame.java} | 32 +- .../channel/map/ChannelMapModel.java | 278 ++++---- .../channel/map/ChannelMapNode.java | 103 --- src/controller/channel/map/ChannelRange.java | 2 +- .../channel/map/ChannelRangeModel.java | 181 +++++ src/controller/site/SiteEditor.java | 101 --- src/controller/site/SiteNode.java | 181 ----- src/controller/system/SystemEditor.java | 101 --- src/controller/system/SystemListNode.java | 102 --- src/controller/system/SystemNode.java | 184 ----- src/gui/SDRTrunk.java | 50 +- src/instrument/gui/DecoderSelectionFrame.java | 4 +- src/instrument/gui/DecoderViewFrame.java | 11 +- src/module/decode/DecodeComponentEditor.java | 10 +- src/module/decode/DecoderFactory.java | 22 +- .../decode/mpt1327/MPT1327ConfigEditor.java | 44 +- src/module/decode/p25/P25Decoder.java | 2 +- .../decode/p25/P25MessageProcessor.java | 2 +- src/module/decode/p25/P25_C4FMDecoder.java | 2 +- src/module/decode/p25/P25_LSMDecoder.java | 2 +- src/module/decode/state/ChannelList.java | 6 +- src/module/decode/state/ChannelPanel.java | 7 +- src/playlist/PlaylistManager.java | 347 +++------- src/playlist/PlaylistNode.java | 168 ----- src/playlist/PlaylistV2.java | 77 +++ .../version1}/AliasDirectory.java | 27 +- src/playlist/version1/AliasListOld.java | 583 ++++++++++++++++ .../version1}/ChannelMapList.java | 5 +- src/{alias => playlist/version1}/Group.java | 5 +- .../version1/PlaylistConverterV1ToV2.java | 168 +++++ .../PlaylistV1.java} | 52 +- .../site => playlist/version1}/Site.java | 3 +- .../system => playlist/version1}/System.java | 5 +- .../version1}/SystemList.java | 5 +- src/source/tuner/TunerNode.java | 6 +- src/spectrum/SpectralDisplayPanel.java | 9 +- src/spectrum/SpectrumFrame.java | 4 +- 75 files changed, 2154 insertions(+), 5008 deletions(-) delete mode 100644 src/alias/AliasDirectoryNode.java delete mode 100644 src/alias/AliasListNode.java delete mode 100644 src/alias/AliasNode.java delete mode 100644 src/alias/GroupEditor.java delete mode 100644 src/alias/GroupNode.java delete mode 100644 src/alias/ListEditor.java delete mode 100644 src/alias/action/AliasActionNode.java delete mode 100644 src/alias/action/beep/BeepActionNode.java delete mode 100644 src/alias/action/clip/ClipActionNode.java delete mode 100644 src/alias/action/script/ScriptActionNode.java delete mode 100644 src/alias/id/esn/ESNNode.java delete mode 100644 src/alias/id/fleetsync/FleetsyncIDNode.java delete mode 100644 src/alias/id/lojack/LoJackIDNode.java delete mode 100644 src/alias/id/mdc/MDC1200IDNode.java delete mode 100644 src/alias/id/mobileID/MINNode.java delete mode 100644 src/alias/id/mpt1327/MPT1327IDNode.java delete mode 100644 src/alias/id/nonrecordable/NonRecordableNode.java delete mode 100644 src/alias/id/priority/PriorityNode.java delete mode 100644 src/alias/id/siteID/SiteIDNode.java delete mode 100644 src/alias/id/status/StatusIDNode.java delete mode 100644 src/alias/id/talkgroup/TalkgroupIDNode.java delete mode 100644 src/alias/id/uniqueID/UniqueIDNode.java delete mode 100644 src/controller/channel/ChannelNode.java delete mode 100644 src/controller/channel/OldChannelEditor.java create mode 100644 src/controller/channel/map/ChannelMapEvent.java delete mode 100644 src/controller/channel/map/ChannelMapListNode.java create mode 100644 src/controller/channel/map/ChannelMapManager.java rename src/{alias/id/AliasIDNode.java => controller/channel/map/ChannelMapManagerFrame.java} (56%) delete mode 100644 src/controller/channel/map/ChannelMapNode.java create mode 100644 src/controller/channel/map/ChannelRangeModel.java delete mode 100644 src/controller/site/SiteEditor.java delete mode 100644 src/controller/site/SiteNode.java delete mode 100644 src/controller/system/SystemEditor.java delete mode 100644 src/controller/system/SystemListNode.java delete mode 100644 src/controller/system/SystemNode.java delete mode 100644 src/playlist/PlaylistNode.java create mode 100644 src/playlist/PlaylistV2.java rename src/{alias => playlist/version1}/AliasDirectory.java (76%) create mode 100644 src/playlist/version1/AliasListOld.java rename src/{controller/channel/map => playlist/version1}/ChannelMapList.java (96%) rename src/{alias => playlist/version1}/Group.java (96%) create mode 100644 src/playlist/version1/PlaylistConverterV1ToV2.java rename src/playlist/{Playlist.java => version1/PlaylistV1.java} (78%) rename src/{controller/site => playlist/version1}/Site.java (98%) rename src/{controller/system => playlist/version1}/System.java (97%) rename src/{controller/system => playlist/version1}/SystemList.java (94%) diff --git a/src/alias/AliasDirectoryNode.java b/src/alias/AliasDirectoryNode.java deleted file mode 100644 index c44d32b9..00000000 --- a/src/alias/AliasDirectoryNode.java +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import settings.SettingsManager; -import controller.ConfigurableNode; - -public class AliasDirectoryNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private PlaylistManager mPlaylistManager; - - private SettingsManager mSettingsManager; - - public AliasDirectoryNode( PlaylistManager playlistManager, - SettingsManager settingsManager, - AliasDirectory directory ) - { - super( playlistManager, directory ); - - mPlaylistManager = playlistManager; - - mSettingsManager = settingsManager; - } - - public AliasDirectory getAliasDirectory() - { - return (AliasDirectory)getUserObject(); - } - - public void init() - { - for( AliasList list: getAliasDirectory().getAliasList() ) - { - AliasListNode node = new AliasListNode( mPlaylistManager, mSettingsManager, list ); - - getModel().addNode( node, AliasDirectoryNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return "Alias Lists [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addItem = new JMenuItem( "Add Alias List" ); - addItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AliasList list = new AliasList(); - - getAliasDirectory().addAliasList( list ); - - AliasListNode node = new AliasListNode( mPlaylistManager, mSettingsManager, list ); - - getModel().addNode( node, - AliasDirectoryNode.this, - AliasDirectoryNode.this.getChildCount() ); - - sort(); - - getModel().showNode( node ); - } - } ); - - retVal.add( addItem ); - - return retVal; - } -} diff --git a/src/alias/AliasList.java b/src/alias/AliasList.java index e62ec72f..59f7dc9a 100644 --- a/src/alias/AliasList.java +++ b/src/alias/AliasList.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,21 +19,17 @@ package alias; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSeeAlso; import module.decode.lj1200.LJ1200Message.Function; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import alias.action.AliasAction; -import alias.action.beep.BeepAction; -import alias.action.clip.ClipAction; -import alias.action.script.ScriptAction; +import sample.Listener; import alias.id.AliasID; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; @@ -41,48 +37,17 @@ import alias.id.lojack.LoJackFunctionAndID; import alias.id.mdc.MDC1200ID; import alias.id.mobileID.Min; import alias.id.mpt1327.MPT1327ID; -import alias.id.nonrecordable.NonRecordable; -import alias.id.priority.Priority; import alias.id.siteID.SiteID; import alias.id.status.StatusID; import alias.id.talkgroup.TalkgroupID; import alias.id.uniqueID.UniqueID; import audio.metadata.MetadataType; -@XmlSeeAlso( { Alias.class, - - /* Alias ID's */ - AliasID.class, - FleetsyncID.class, - Esn.class, - Group.class, - LoJackFunctionAndID.class, - MDC1200ID.class, - Min.class, - MPT1327ID.class, - UniqueID.class, - SiteID.class, - StatusID.class, - TalkgroupID.class, - - /* Alias Attributes */ - NonRecordable.class, - Priority.class, - - /* Alias Actions */ - AliasAction.class, - BeepAction.class, - ClipAction.class, - ScriptAction.class } ) - -@XmlRootElement( name = "alias_list" ) -public class AliasList implements Comparable +public class AliasList implements Listener { private final static Logger mLog = LoggerFactory.getLogger( AliasList.class ); - private String mName; - private ArrayList mGroups = new ArrayList(); - + private List mAliases = new ArrayList<>(); private Map mESN = new HashMap<>(); private Map mFleetsync = new HashMap<>(); private Map mLoJack = new HashMap<>(); @@ -102,167 +67,326 @@ public class AliasList implements Comparable private boolean mSiteWildcard = false; private boolean mTalkgroupWildcard = false; - public AliasList() - { - this( null ); - } + private String mName; + /** + * List of aliases where all aliases share the same list name. Contains + * several methods for alias lookup from identifier values, like talkgroups. + * + * Responds to alias change events to keep the internal alias list updated. + */ public AliasList( String name ) { mName = name; - update(); } - + /** - * Load/Reload all lookup hashmaps + * Adds the alias to this list */ - public void update() + public void addAlias( Alias alias ) { - //Clear hashmaps - mESN.clear(); - mFleetsync.clear(); - mLoJack.clear(); - mMDC1200.clear(); - mMobileID.clear(); - mMPT1327.clear(); - mSiteID.clear(); - mStatus.clear(); - mTalkgroup.clear(); - mUniqueID.clear(); - - for( Group group: mGroups ) + if( alias != null ) { - for( Alias alias: group.getAlias() ) + mAliases.add( alias ); + + for( AliasID aliasID: alias.getId() ) { - for( AliasID id: alias.getId() ) + addAliasID( aliasID, alias ); + } + } + } + + /** + * Adds the alias and alias identifier to the internal type mapping. + */ + private void addAliasID( AliasID id, Alias alias ) + { + switch( id.getType() ) + { + case ESN: + Esn esn = (Esn)id; + if( esn.getEsn().contains( "*" ) ) { - switch( id.getType() ) + mESNWildcard = true; + + mESN.put( fixWildcard( esn.getEsn() ), alias ); + } + else + { + mESN.put( esn.getEsn(), alias ); + } + break; + case Fleetsync: + FleetsyncID fs = (FleetsyncID)id; + + if( fs.getIdent().contains( "*" ) ) + { + mFleetsyncWildcard = true; + + mFleetsync.put( fixWildcard( fs.getIdent() ), alias ); + } + else + { + mFleetsync.put( fs.getIdent(), alias ); + } + break; + case LoJack: + mLoJack.put( (LoJackFunctionAndID)id, alias ); + break; + case MDC1200: + MDC1200ID mdc = (MDC1200ID)id; + + if( mdc.getIdent().contains( "*" ) ) + { + mMDC1200Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMDC1200.put( fixWildcard( mdc.getIdent() ), alias ); + } + else + { + mMDC1200.put( mdc.getIdent(), alias ); + } + break; + case MPT1327: + MPT1327ID mpt = (MPT1327ID)id; + + String ident = mpt.getIdent(); + + if( ident != null ) + { + if( ident.contains( "*" ) ) { - case ESN: - Esn esn = (Esn)id; - if( esn.getEsn().contains( "*" ) ) - { - mESNWildcard = true; - - mESN.put( fixWildcard( esn.getEsn() ), alias ); - } - else - { - mESN.put( esn.getEsn(), alias ); - } - break; - case Fleetsync: - FleetsyncID fs = (FleetsyncID)id; - - if( fs.getIdent().contains( "*" ) ) - { - mFleetsyncWildcard = true; - - mFleetsync.put( fixWildcard( fs.getIdent() ), alias ); - } - else - { - mFleetsync.put( fs.getIdent(), alias ); - } - break; - case LoJack: - mLoJack.put( (LoJackFunctionAndID)id, alias ); - break; - case MDC1200: - MDC1200ID mdc = (MDC1200ID)id; - - if( mdc.getIdent().contains( "*" ) ) - { - mMDC1200Wildcard = true; - - //Replace (*) wildcard with regex wildcard (.) - mMDC1200.put( fixWildcard( mdc.getIdent() ), alias ); - } - else - { - mMDC1200.put( mdc.getIdent(), alias ); - } - break; - case MPT1327: - MPT1327ID mpt = (MPT1327ID)id; - - String ident = mpt.getIdent(); - - if( ident != null ) - { - if( ident.contains( "*" ) ) - { - mMPT1327Wildcard = true; - - //Replace (*) wildcard with regex wildcard (.) - mMPT1327.put( fixWildcard( ident ), alias ); - } - else - { - mMPT1327.put( ident, alias ); - } - } - break; - case MIN: - Min min = (Min)id; - - if( min.getMin().contains( "*" ) ) - { - mMobileIDWildcard = true; - mMobileID.put( fixWildcard( min.getMin() ), alias ); - } - else - { - mMobileID.put( min.getMin(), alias ); - } - break; - case LTRNetUID: - UniqueID uid = (UniqueID)id; - - mUniqueID.put( uid.getUid(), alias ); - break; - case Site: - SiteID siteID = (SiteID)id; - - if( siteID.getSite().contains( "*" ) ) - { - mSiteWildcard = true; - mSiteID.put( fixWildcard( siteID.getSite() ), alias ); - } - else - { - mSiteID.put( siteID.getSite(), alias ); - } - - break; - case Status: - mStatus.put( ((StatusID)id).getStatus(), alias ); - break; - case Talkgroup: - TalkgroupID tgid = (TalkgroupID)id; - - if( tgid.getTalkgroup().contains( "*" ) ) - { - mTalkgroupWildcard = true; - - //Replace (*) wildcard with regex wildcard (.) - mTalkgroup.put( fixWildcard( tgid.getTalkgroup() ), alias ); - } - else - { - mTalkgroup.put( tgid.getTalkgroup(), alias ); - } - break; - case NonRecordable: - case Priority: - //We don't maintain lookups for these items - break; - default: - mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); - break; + mMPT1327Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMPT1327.put( fixWildcard( ident ), alias ); + } + else + { + mMPT1327.put( ident, alias ); } } + break; + case MIN: + Min min = (Min)id; + + if( min.getMin().contains( "*" ) ) + { + mMobileIDWildcard = true; + mMobileID.put( fixWildcard( min.getMin() ), alias ); + } + else + { + mMobileID.put( min.getMin(), alias ); + } + break; + case LTRNetUID: + UniqueID uid = (UniqueID)id; + + mUniqueID.put( uid.getUid(), alias ); + break; + case Site: + SiteID siteID = (SiteID)id; + + if( siteID.getSite().contains( "*" ) ) + { + mSiteWildcard = true; + mSiteID.put( fixWildcard( siteID.getSite() ), alias ); + } + else + { + mSiteID.put( siteID.getSite(), alias ); + } + + break; + case Status: + mStatus.put( ((StatusID)id).getStatus(), alias ); + break; + case Talkgroup: + TalkgroupID tgid = (TalkgroupID)id; + + if( tgid.getTalkgroup().contains( "*" ) ) + { + mTalkgroupWildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mTalkgroup.put( fixWildcard( tgid.getTalkgroup() ), alias ); + } + else + { + mTalkgroup.put( tgid.getTalkgroup(), alias ); + } + break; + case NonRecordable: + case Priority: + //We don't maintain lookups for these items + break; + default: + mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); + break; + } + } + + /** + * Removes the alias from this list + */ + public void removeAlias( Alias alias ) + { + if( alias != null ) + { + for( AliasID aliasID: alias.getId() ) + { + removeAliasID( aliasID, alias ); } + + mAliases.remove( alias ); + } + } + + /** + * Removes the alias and alias identifier from internal mappings. + */ + private void removeAliasID( AliasID id, Alias alias ) + { + switch( id.getType() ) + { + case ESN: + Esn esn = (Esn)id; + if( mESN.containsKey( esn.getEsn() ) ) + { + Alias esnAlias = mESN.get( esn.getEsn() ); + + if( esnAlias != null && esnAlias == alias ) + { + mESN.remove( esn.getEsn() ); + } + } + break; + case Fleetsync: + FleetsyncID fs = (FleetsyncID)id; + + if( mFleetsync.containsKey( fs.getIdent() ) ) + { + Alias fsAlias = mFleetsync.get( fs.getIdent() ); + + if( fsAlias != null && fsAlias == alias ) + { + mFleetsync.remove( fs.getIdent() ); + } + } + break; + case LoJack: + LoJackFunctionAndID lj = (LoJackFunctionAndID)id; + + if( mLoJack.containsKey( lj ) ) + { + Alias ljAlias = mLoJack.get( lj ); + + if( ljAlias != null && ljAlias == alias ) + { + mLoJack.remove( lj ); + } + } + break; + case MDC1200: + MDC1200ID mdc = (MDC1200ID)id; + + if( mMDC1200.containsKey( mdc.getIdent() ) ) + { + Alias mdcAlias = mMDC1200.get( mdc.getIdent() ); + + if( mdcAlias != null && mdcAlias == alias ) + { + mMDC1200.remove( mdc.getIdent() ); + } + } + break; + case MPT1327: + MPT1327ID mpt = (MPT1327ID)id; + + if( mMPT1327.containsKey( mpt.getIdent() ) ) + { + Alias mptAlias = mMDC1200.get( mpt.getIdent() ); + + if( mptAlias != null && mptAlias == alias ) + { + mMPT1327.remove( mpt.getIdent() ); + } + } + break; + case MIN: + Min min = (Min)id; + + if( mMobileID.containsKey( min.getMin() ) ) + { + Alias minAlias = mMobileID.get( min.getMin() ); + + if( minAlias != null && minAlias == alias ) + { + mMobileID.remove( min.getMin() ); + } + } + break; + case LTRNetUID: + UniqueID uid = (UniqueID)id; + + if( mUniqueID.containsKey( uid.getUid() ) ) + { + Alias uidAlias = mUniqueID.get( uid.getUid() ); + + if( uidAlias != null && uidAlias == alias ) + { + mUniqueID.remove( uid.getUid() ); + } + } + break; + case Site: + SiteID sid = (SiteID)id; + + if( mSiteID.containsKey( sid.getSite() ) ) + { + Alias sidAlias = mSiteID.get( sid.getSite() ); + + if( sidAlias != null && sidAlias == alias ) + { + mSiteID.remove( sid.getSite() ); + } + } + break; + case Status: + StatusID stat = (StatusID)id; + + if( mStatus.containsKey( stat.getStatus() ) ) + { + Alias statAlias = mStatus.get( stat.getStatus() ); + + if( statAlias != null && statAlias == alias ) + { + mStatus.remove( stat.getStatus() ); + } + } + break; + case Talkgroup: + TalkgroupID tg = (TalkgroupID)id; + + if( mTalkgroup.containsKey( tg.getTalkgroup() ) ) + { + Alias tgAlias = mTalkgroup.get( tg.getTalkgroup() ); + + if( tgAlias != null && tgAlias == alias ) + { + mTalkgroup.remove( tg.getTalkgroup() ); + } + } + break; + case NonRecordable: + case Priority: + //We don't maintain lookups for these items + break; + default: + mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); + break; } } @@ -279,7 +403,10 @@ public class AliasList implements Comparable return value; } - + + /** + * Lookup alias by site ID + */ public Alias getSiteID( String siteID ) { if( siteID != null ) @@ -302,17 +429,26 @@ public class AliasList implements Comparable return null; } - + + /** + * Lookup alias by status ID + */ public Alias getStatus( int status ) { return mStatus.get( status ); } + /** + * Lookup alias by Unique ID (UID) + */ public Alias getUniqueID( int uniqueID ) { return mUniqueID.get( uniqueID ); } + /** + * Lookup alias by ESN + */ public Alias getESNAlias( String esn ) { if( esn != null ) @@ -336,7 +472,9 @@ public class AliasList implements Comparable return null; } - + /** + * Lookup alias by fleetsync ID + */ public Alias getFleetsyncAlias( String ident ) { if( ident != null ) @@ -360,6 +498,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by lojack function and ID + */ public Alias getLoJackAlias( Function function, String id ) { if( id != null ) @@ -376,6 +517,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by MDC1200 ID + */ public Alias getMDC1200Alias( String ident ) { if( ident != null ) @@ -399,6 +543,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by MPT1327 Ident + */ public Alias getMPT1327Alias( String ident ) { if( ident != null ) @@ -422,22 +569,9 @@ public class AliasList implements Comparable return null; } - public Group getGroup( Alias alias ) - { - if( alias != null ) - { - for( Group group: mGroups ) - { - if( group.contains( alias ) ) - { - return group; - } - } - } - - return null; - } - + /** + * Lookup alias by Mobile ID Number (MIN) + */ public Alias getMobileIDNumberAlias( String ident ) { if( ident != null ) @@ -461,6 +595,9 @@ public class AliasList implements Comparable return null; } + /** + * Lookup alias by talkgroup + */ public Alias getTalkgroupAlias( String tgid ) { if( tgid != null ) @@ -483,49 +620,27 @@ public class AliasList implements Comparable return null; } - + + /** + * Alias list name + */ public String toString() { return mName; } + /** + * Alias list name + */ @XmlAttribute public String getName() { return mName; } - - public void setName( String name ) - { - mName = name; - } - - public ArrayList getGroup() - { - return mGroups; - } - - public void setGroup( ArrayList groups ) - { - mGroups = groups; - } - - public void addGroup( Group group ) - { - mGroups.add( group ); - } - - public void removeGroup( Group group ) - { - mGroups.remove( group ); - } - - @Override - public int compareTo( AliasList otherAliasList ) - { - return getName().compareTo( otherAliasList.getName() ); - } + /** + * Lookup an alias by metadata type and string value + */ public Alias getAlias( String value, MetadataType type ) { switch( type ) @@ -577,5 +692,48 @@ public class AliasList implements Comparable return null; } - + + /** + * Receive alias change event notifications and modify this list accordingly + */ + @Override + public void receive( AliasEvent event ) + { + Alias alias = event.getAlias(); + + switch( event.getEvent() ) + { + case ADD: + if( alias.getList() != null && + getName().equalsIgnoreCase( alias.getList() ) ) + { + addAlias( alias ); + } + break; + case CHANGE: + if( alias.getList() != null && + getName().equalsIgnoreCase( alias.getList() ) ) + { + if( !mAliases.contains( alias ) ) + { + //Alias was moved to this list - add it + addAlias( alias ); + } + } + else if( mAliases.contains( alias ) ) + { + //An alias in this list was moved to another list + removeAlias( alias ); + } + break; + case DELETE: + if( mAliases.contains( alias ) ) + { + removeAlias( alias ); + } + break; + default: + break; + } + } } diff --git a/src/alias/AliasListNode.java b/src/alias/AliasListNode.java deleted file mode 100644 index 573708ce..00000000 --- a/src/alias/AliasListNode.java +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; - -import playlist.PlaylistManager; -import settings.SettingsManager; -import controller.ConfigurableNode; - -public class AliasListNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private PlaylistManager mPlaylistManager; - private SettingsManager mSettingsManager; - - public AliasListNode( PlaylistManager playlistManager, - SettingsManager settingsManager, - AliasList list ) - { - super( playlistManager, list ); - - mPlaylistManager = playlistManager; - mSettingsManager = settingsManager; - } - - @Override - public JPanel getEditor() - { - return new ListEditor( this ); - } - - public AliasList getList() - { - return (AliasList)getUserObject(); - } - - public void init() - { - for( Group group: getList().getGroup() ) - { - GroupNode node = new GroupNode( mPlaylistManager, mSettingsManager, group ); - - getModel().addNode( node, AliasListNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return getList().getName() + " [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addItem = new JMenuItem( "Add Group" ); - addItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Group group = new Group(); - - getList().addGroup( group ); - - GroupNode node = new GroupNode( mPlaylistManager, mSettingsManager, group ); - - getModel().addNode( node, - AliasListNode.this, - AliasListNode.this.getChildCount() ); - - sort(); - - getModel().showNode( node ); - } - } ); - - retVal.add( addItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasDirectoryNode parent = (AliasDirectoryNode)getParent(); - - parent.getAliasDirectory().removeAliasList( getList() ); - - save(); - - getModel().removeNodeFromParent( AliasListNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/AliasModel.java b/src/alias/AliasModel.java index 9e346269..54a32b13 100644 --- a/src/alias/AliasModel.java +++ b/src/alias/AliasModel.java @@ -19,7 +19,9 @@ package alias; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.table.AbstractTableModel; @@ -48,6 +50,7 @@ public class AliasModel extends AbstractTableModel private List mAliases = new CopyOnWriteArrayList<>(); private Broadcaster mAliasEventBroadcaster = new Broadcaster<>(); + private Map mAliasListMap = new HashMap<>(); public AliasModel() { @@ -71,6 +74,31 @@ public class AliasModel extends AbstractTableModel return null; } + public AliasList getAliasList( String name ) + { + if( mAliasListMap.containsKey( name ) ) + { + return mAliasListMap.get( name ); + } + + AliasList aliasList = new AliasList( name ); + + for( Alias alias: mAliases ) + { + if( alias.getList().equalsIgnoreCase( name ) ) + { + aliasList.addAlias( alias ); + } + } + + mAliasListMap.put( name, aliasList ); + + //Register the new alias list to receive updates from this model + addListener( aliasList ); + + return aliasList; + } + /** * Returns a list of unique alias list names from across the alias set */ diff --git a/src/alias/AliasNode.java b/src/alias/AliasNode.java deleted file mode 100644 index 2c5ec25b..00000000 --- a/src/alias/AliasNode.java +++ /dev/null @@ -1,572 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import map.MapIcon; -import playlist.PlaylistManager; -import settings.SettingsManager; -import alias.action.AliasAction; -import alias.action.beep.BeepAction; -import alias.action.beep.BeepActionNode; -import alias.action.clip.ClipAction; -import alias.action.clip.ClipActionNode; -import alias.action.script.ScriptAction; -import alias.action.script.ScriptActionNode; -import alias.id.AliasID; -import alias.id.esn.ESNNode; -import alias.id.esn.Esn; -import alias.id.fleetsync.FleetsyncID; -import alias.id.fleetsync.FleetsyncIDNode; -import alias.id.lojack.LoJackFunctionAndID; -import alias.id.lojack.LoJackIDNode; -import alias.id.mdc.MDC1200ID; -import alias.id.mdc.MDC1200IDNode; -import alias.id.mobileID.MINNode; -import alias.id.mobileID.Min; -import alias.id.mpt1327.MPT1327ID; -import alias.id.mpt1327.MPT1327IDNode; -import alias.id.nonrecordable.NonRecordable; -import alias.id.nonrecordable.NonRecordableNode; -import alias.id.priority.Priority; -import alias.id.priority.PriorityNode; -import alias.id.siteID.SiteID; -import alias.id.siteID.SiteIDNode; -import alias.id.status.StatusID; -import alias.id.status.StatusIDNode; -import alias.id.talkgroup.TalkgroupID; -import alias.id.talkgroup.TalkgroupIDNode; -import alias.id.uniqueID.UniqueID; -import alias.id.uniqueID.UniqueIDNode; -import controller.ConfigurableNode; - -public class AliasNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private SettingsManager mSettingsManager; - private PlaylistManager mPlaylistManager; - - public AliasNode( PlaylistManager playlistManager, - SettingsManager settingsManager, - Alias alias ) - { - super( playlistManager, alias ); - - mSettingsManager = settingsManager; - } - - @Override - public JPanel getEditor() - { - return null; -// return new AliasEditor( this, mSettingsManager ); - } - - public Alias getAlias() - { - return (Alias)getUserObject(); - } - - public String getIconPath() - { - String icon = getAlias().getIconName(); - - MapIcon mapIcon = mSettingsManager.getMapIcon( icon ); - - if( mapIcon != null ) - { - return mapIcon.getPath(); - } - else - { - return null; - } - } - - public void init() - { - for( AliasID aliasID: getAlias().getId() ) - { - switch( aliasID.getType() ) - { - case ESN: - getModel().addNode( new ESNNode( mPlaylistManager, (Esn)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Fleetsync: - getModel().addNode( new FleetsyncIDNode( mPlaylistManager, (FleetsyncID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case LoJack: - getModel().addNode( new LoJackIDNode( mPlaylistManager, (LoJackFunctionAndID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case LTRNetUID: - getModel().addNode( new UniqueIDNode( mPlaylistManager, (UniqueID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case MDC1200: - getModel().addNode( new MDC1200IDNode( mPlaylistManager, (MDC1200ID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case MIN: - getModel().addNode( new MINNode( mPlaylistManager, (Min)aliasID ), - AliasNode.this, getChildCount() ); - break; - case MPT1327: - getModel().addNode( new MPT1327IDNode( mPlaylistManager, (MPT1327ID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case NonRecordable: - getModel().addNode( new NonRecordableNode( mPlaylistManager, (NonRecordable)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Priority: - getModel().addNode( new PriorityNode( mPlaylistManager, (Priority)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Site: - getModel().addNode( new SiteIDNode( mPlaylistManager, (SiteID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Status: - getModel().addNode( new StatusIDNode( mPlaylistManager, (StatusID)aliasID ), - AliasNode.this, getChildCount() ); - break; - case Talkgroup: - getModel().addNode( new TalkgroupIDNode( mPlaylistManager, (TalkgroupID)aliasID ), - AliasNode.this, getChildCount() ); - break; - default: - throw new IllegalArgumentException( "Unrecognized alias ID [" + - aliasID.getType() + "]"); - } - } - - for( AliasAction action: getAlias().getAction() ) - { - if( action instanceof BeepAction ) - { - getModel().addNode( new BeepActionNode( mPlaylistManager, (BeepAction)action ), - AliasNode.this, getChildCount() ); - } - else if( action instanceof ClipAction ) - { - getModel().addNode( new ClipActionNode( mPlaylistManager, (ClipAction)action ), - AliasNode.this, getChildCount() ); - } - else if( action instanceof ScriptAction ) - { - getModel().addNode( new ScriptActionNode( mPlaylistManager, (ScriptAction)action ), - AliasNode.this, getChildCount() ); - } - } - - sort(); - } - - public String toString() - { - return getAlias().getName() + " [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenu addIDMenu = new JMenu( "Add ID" ); - - JMenuItem addESNItem = new JMenuItem( "ESN" ); - addESNItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Esn esn = new Esn(); - - getAlias().addAliasID( esn ); - - ESNNode node = new ESNNode( mPlaylistManager, esn ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addESNItem ); - - JMenuItem addFleetsyncItem = new JMenuItem( "Fleetsync" ); - addFleetsyncItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FleetsyncID fs = new FleetsyncID(); - - getAlias().addAliasID( fs ); - - FleetsyncIDNode node = new FleetsyncIDNode( mPlaylistManager, fs ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addFleetsyncItem ); - - JMenuItem addLoJackItem = new JMenuItem( "LoJack" ); - addLoJackItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - LoJackFunctionAndID lj = new LoJackFunctionAndID(); - - getAlias().addAliasID( lj ); - - LoJackIDNode node = new LoJackIDNode( mPlaylistManager, lj ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addLoJackItem ); - - JMenuItem addMDCItem = new JMenuItem( "MDC-1200" ); - addMDCItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MDC1200ID mdc = new MDC1200ID(); - - getAlias().addAliasID( mdc ); - - MDC1200IDNode node = new MDC1200IDNode( mPlaylistManager, mdc ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addMDCItem ); - - JMenuItem addMINItem = new JMenuItem( "MIN" ); - addMINItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Min min = new Min(); - - getAlias().addAliasID( min ); - - MINNode node = new MINNode( mPlaylistManager, min ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addMINItem ); - - JMenuItem addMPTItem = new JMenuItem( "MPT-1327" ); - addMPTItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MPT1327ID mpt = new MPT1327ID(); - - getAlias().addAliasID( mpt ); - - MPT1327IDNode node = new MPT1327IDNode( mPlaylistManager, mpt ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addMPTItem ); - - JMenuItem addSiteItem = new JMenuItem( "Site" ); - addSiteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SiteID siteID = new SiteID(); - - getAlias().addAliasID( siteID ); - - SiteIDNode node = new SiteIDNode( mPlaylistManager, siteID ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addSiteItem ); - - JMenuItem addStatusItem = new JMenuItem( "Status" ); - addStatusItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - StatusID statusID = new StatusID(); - - getAlias().addAliasID( statusID ); - - StatusIDNode node = new StatusIDNode( mPlaylistManager, statusID ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addIDMenu.add( addStatusItem ); - - JMenuItem addTalkgroupItem = new JMenuItem( "Talkgroup" ); - addTalkgroupItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - TalkgroupID tg = new TalkgroupID(); - - getAlias().addAliasID( tg ); - - TalkgroupIDNode node = new TalkgroupIDNode( mPlaylistManager, tg ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addTalkgroupItem ); - - JMenuItem addUniqueIDItem = new JMenuItem( "Unique ID" ); - addUniqueIDItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - UniqueID uid = new UniqueID(); - - getAlias().addAliasID( uid ); - - UniqueIDNode node = new UniqueIDNode( mPlaylistManager, uid ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - addIDMenu.add( addUniqueIDItem ); - - retVal.add( addIDMenu ); - - JMenu addActionMenu = new JMenu( "Add Action" ); - - JMenuItem addClipItem = new JMenuItem( "Audio Clip" ); - - addClipItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ClipAction clipAction = new ClipAction(); - - getAlias().addAliasAction( clipAction ); - - ClipActionNode node = new ClipActionNode( mPlaylistManager, clipAction ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addActionMenu.add( addClipItem ); - - JMenuItem addBeepItem = new JMenuItem( "Beep" ); - - addBeepItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - BeepAction beepAction = new BeepAction(); - - getAlias().addAliasAction( beepAction ); - - BeepActionNode node = new BeepActionNode( mPlaylistManager, beepAction ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addActionMenu.add( addBeepItem ); - - JMenuItem addScriptItem = new JMenuItem( "Script" ); - - addScriptItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ScriptAction scriptAction = new ScriptAction(); - - getAlias().addAliasAction( scriptAction ); - - ScriptActionNode node = new ScriptActionNode( mPlaylistManager, scriptAction ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - addActionMenu.add( addScriptItem ); - - retVal.add( addActionMenu ); - - retVal.addSeparator(); - - if( getAlias().isRecordable() ) - { - JMenuItem nonRecordableItem = new JMenuItem( "Set Non-Recordable" ); - - nonRecordableItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - NonRecordable non = new NonRecordable(); - - getAlias().addAliasID( non ); - - NonRecordableNode node = new NonRecordableNode( mPlaylistManager, non ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - retVal.add( nonRecordableItem ); - retVal.addSeparator(); - } - - if( !getAlias().hasCallPriority() ) - { - JMenuItem priorityItem = new JMenuItem( "Set Call Priority" ); - - priorityItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Priority priority = new Priority(); - - getAlias().addAliasID( priority ); - - PriorityNode node = new PriorityNode( mPlaylistManager, priority ); - - getModel().addNode( node, - AliasNode.this, - AliasNode.this.getChildCount() ); - - node.show(); - } - } ); - - retVal.add( priorityItem ); - retVal.addSeparator(); - } - - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - GroupNode parent = (GroupNode)getParent(); - - parent.getGroup().removeAlias( getAlias() ); - - save(); - - getModel().removeNodeFromParent( AliasNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/GroupEditor.java b/src/alias/GroupEditor.java deleted file mode 100644 index 6cb6ab1d..00000000 --- a/src/alias/GroupEditor.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; - -public class GroupEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private GroupNode mGroupNode; - - private JLabel mLabelName; - private JTextField mTextGroup; - - public GroupEditor( GroupNode groupNode ) - { - mGroupNode = groupNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "", "[right][grow,fill]", "[][][grow]" ) ); - - setBorder( BorderFactory.createTitledBorder( "Group" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName, "align right" ); - - mTextGroup = new JTextField( mGroupNode.getGroup().getName() ); - add( mTextGroup, "wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( GroupEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( GroupEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - String group = mTextGroup.getText(); - - if( group != null ) - { - boolean expanded = mGroupNode.getModel().getTree() - .isExpanded( new TreePath( mGroupNode ) ); - - mGroupNode.getGroup().setName( group ); - - ((ConfigurableNode)mGroupNode.getParent()).sort(); - - mGroupNode.save(); - - mGroupNode.show(); - - if( expanded ) - { - mGroupNode.getModel().getTree() - .expandPath( new TreePath( mGroupNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( GroupEditor.this, "Please enter a group name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextGroup.setText( mGroupNode.getGroup().getName() ); - } - - mGroupNode.refresh(); - } -} diff --git a/src/alias/GroupNode.java b/src/alias/GroupNode.java deleted file mode 100644 index f8d52904..00000000 --- a/src/alias/GroupNode.java +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; - -import playlist.PlaylistManager; -import settings.SettingsManager; -import controller.ConfigurableNode; - -public class GroupNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private PlaylistManager mPlaylistManager; - private SettingsManager mSettingsManager; - - public GroupNode( PlaylistManager playlistManager, - SettingsManager settingsManager, - Group group ) - { - super( playlistManager, group ); - - mSettingsManager = settingsManager; - } - - @Override - public JPanel getEditor() - { - return new GroupEditor( this ); - } - - public Group getGroup() - { - return (Group)getUserObject(); - } - - public void init() - { - for( Alias alias: getGroup().getAlias() ) - { - AliasNode node = new AliasNode( mPlaylistManager, mSettingsManager, alias ); - - getModel().addNode( node, GroupNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return getGroup().getName() + " [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addItem = new JMenuItem( "Add Alias" ); - addItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Alias alias = new Alias(); - - getGroup().addAlias( alias ); - - AliasNode node = new AliasNode( mPlaylistManager, mSettingsManager, alias ); - - getModel().addNode( node, - GroupNode.this, - GroupNode.this.getChildCount() ); - - sort(); - - getModel().showNode( node ); - } - } ); - - retVal.add( addItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasListNode parent = (AliasListNode)getParent(); - - parent.getList().removeGroup( getGroup() ); - - save(); - - getModel().removeNodeFromParent( GroupNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/ListEditor.java b/src/alias/ListEditor.java deleted file mode 100644 index b4649a5c..00000000 --- a/src/alias/ListEditor.java +++ /dev/null @@ -1,107 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; -import controller.ConfigurableNode; - -public class ListEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private AliasListNode mListNode; - - private JLabel mLabelName; - private JTextField mTextGroup; - - public ListEditor( AliasListNode listNode ) - { - mListNode = listNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "", "[right][grow,fill]", "[][][grow]" ) ); - - setBorder( BorderFactory.createTitledBorder( "List" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName, "align right" ); - - mTextGroup = new JTextField( mListNode.getList().getName() ); - add( mTextGroup, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ListEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( ListEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - String group = mTextGroup.getText(); - - if( group != null ) - { - boolean expanded = mListNode.getModel().getTree() - .isExpanded( new TreePath( mListNode ) ); - - mListNode.getList().setName( group ); - ((ConfigurableNode)mListNode.getParent()).sort(); - mListNode.save(); - - mListNode.show(); - - if( expanded ) - { - mListNode.getModel().getTree().expandPath( new TreePath( mListNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( ListEditor.this, "Please enter a list name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextGroup.setText( mListNode.getList().getName() ); - } - - mListNode.refresh(); - } -} diff --git a/src/alias/action/AliasActionNode.java b/src/alias/action/AliasActionNode.java deleted file mode 100644 index 173ecf07..00000000 --- a/src/alias/action/AliasActionNode.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.action; - -import playlist.PlaylistManager; -import controller.ConfigurableNode; - -public class AliasActionNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private PlaylistManager mPlaylistManager; - - public AliasActionNode( PlaylistManager playlistManager, AliasAction action ) - { - super( playlistManager, action ); - - mPlaylistManager = playlistManager; - } - - public void save() - { - mPlaylistManager.save(); - } - - public String getIconPath() - { - return "images/action.png"; - } -} diff --git a/src/alias/action/beep/BeepActionNode.java b/src/alias/action/beep/BeepActionNode.java deleted file mode 100644 index 96de565e..00000000 --- a/src/alias/action/beep/BeepActionNode.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.action.beep; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.action.AliasActionNode; - -public class BeepActionNode extends AliasActionNode -{ - private static final long serialVersionUID = 1L; - - public BeepActionNode( PlaylistManager playlistManager, BeepAction beepAction ) - { - super( playlistManager, beepAction ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public BeepAction getBeepAction() - { - return (BeepAction)getUserObject(); - } - - public String toString() - { - return "Action: Beep"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasAction( getBeepAction() ); - - /* Save before removing the node ... so that we still have - * a reference to the parent's resources */ - save(); - - getModel().removeNodeFromParent( BeepActionNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/action/clip/ClipActionNode.java b/src/alias/action/clip/ClipActionNode.java deleted file mode 100644 index 3e42a432..00000000 --- a/src/alias/action/clip/ClipActionNode.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.action.clip; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.action.AliasActionNode; - -public class ClipActionNode extends AliasActionNode -{ - private static final long serialVersionUID = 1L; - - public ClipActionNode( PlaylistManager playlistManager, ClipAction clipAction ) - { - super( playlistManager, clipAction ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public ClipAction getClipAction() - { - return (ClipAction)getUserObject(); - } - - public String toString() - { - return "Action: Audio Clip"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasAction( getClipAction() ); - - /* Save before removing the node ... so that we still have - * a reference to the parent's resources */ - save(); - - getModel().removeNodeFromParent( ClipActionNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/action/script/ScriptActionNode.java b/src/alias/action/script/ScriptActionNode.java deleted file mode 100644 index 74d20a44..00000000 --- a/src/alias/action/script/ScriptActionNode.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.action.script; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.action.AliasActionNode; - -public class ScriptActionNode extends AliasActionNode -{ - private static final long serialVersionUID = 1L; - - public ScriptActionNode( PlaylistManager playlistManager, ScriptAction scriptAction ) - { - super( playlistManager, scriptAction ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public ScriptAction getScriptAction() - { - return (ScriptAction)getUserObject(); - } - - public String toString() - { - return "Action: Script"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasAction( getScriptAction() ); - - /* Save before removing the node ... so that we still have - * a reference to the parent's resources */ - save(); - - getModel().removeNodeFromParent( ScriptActionNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/AliasID.java b/src/alias/id/AliasID.java index 63497c91..14345e9b 100644 --- a/src/alias/id/AliasID.java +++ b/src/alias/id/AliasID.java @@ -20,7 +20,7 @@ package alias.id; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; -import alias.Group; +import playlist.version1.Group; import alias.id.esn.Esn; import alias.id.fleetsync.FleetsyncID; import alias.id.lojack.LoJackFunctionAndID; diff --git a/src/alias/id/esn/ESNNode.java b/src/alias/id/esn/ESNNode.java deleted file mode 100644 index b75dc306..00000000 --- a/src/alias/id/esn/ESNNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.esn; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class ESNNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public ESNNode( PlaylistManager playlistManager, Esn esn ) - { - super( playlistManager, esn ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public Esn getESN() - { - return (Esn)getUserObject(); - } - - public String toString() - { - return "ESN " + getESN().getEsn(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getESN() ); - - save(); - - getModel().removeNodeFromParent( ESNNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/fleetsync/FleetsyncIDNode.java b/src/alias/id/fleetsync/FleetsyncIDNode.java deleted file mode 100644 index aae77b47..00000000 --- a/src/alias/id/fleetsync/FleetsyncIDNode.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.fleetsync; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class FleetsyncIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public FleetsyncIDNode( PlaylistManager playlistManager, FleetsyncID id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public FleetsyncID getFleetsyncID() - { - return (FleetsyncID)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "Fleetsync " ); - - String ident = getFleetsyncID().getIdent(); - - if( ident != null ) - { - sb.append( ident ); - } - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getFleetsyncID() ); - - save(); - - getModel().removeNodeFromParent( FleetsyncIDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/lojack/LoJackIDNode.java b/src/alias/id/lojack/LoJackIDNode.java deleted file mode 100644 index 76588b38..00000000 --- a/src/alias/id/lojack/LoJackIDNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.lojack; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class LoJackIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public LoJackIDNode( PlaylistManager playlistManager, LoJackFunctionAndID id ) - { - super( playlistManager,id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public LoJackFunctionAndID getLoJackID() - { - return (LoJackFunctionAndID)getUserObject(); - } - - public String toString() - { - return getLoJackID().toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getLoJackID() ); - - save(); - - getModel().removeNodeFromParent( LoJackIDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/mdc/MDC1200IDNode.java b/src/alias/id/mdc/MDC1200IDNode.java deleted file mode 100644 index 06309d68..00000000 --- a/src/alias/id/mdc/MDC1200IDNode.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.mdc; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class MDC1200IDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public MDC1200IDNode( PlaylistManager playlistManager, MDC1200ID id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public MDC1200ID getMDC1200ID() - { - return (MDC1200ID)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "MDC-1200 " ); - sb.append( getMDC1200ID().getIdent() ); - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getMDC1200ID() ); - - save(); - - getModel().removeNodeFromParent( MDC1200IDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/mobileID/MINNode.java b/src/alias/id/mobileID/MINNode.java deleted file mode 100644 index 2c04c0e8..00000000 --- a/src/alias/id/mobileID/MINNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.mobileID; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class MINNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public MINNode( PlaylistManager playlistManager, Min min ) - { - super( playlistManager, min ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public Min getMIN() - { - return (Min)getUserObject(); - } - - public String toString() - { - return "MIN " + getMIN().getMin(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getMIN() ); - - save(); - - getModel().removeNodeFromParent( MINNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/mpt1327/MPT1327IDNode.java b/src/alias/id/mpt1327/MPT1327IDNode.java deleted file mode 100644 index da648bfe..00000000 --- a/src/alias/id/mpt1327/MPT1327IDNode.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.mpt1327; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class MPT1327IDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public MPT1327IDNode( PlaylistManager playlistManager, MPT1327ID id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public MPT1327ID getMPT1327ID() - { - return (MPT1327ID)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( "MPT-1327 " ); - sb.append( getMPT1327ID().getIdent() ); - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getMPT1327ID() ); - - save(); - - getModel().removeNodeFromParent( MPT1327IDNode.this ); - } - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/alias/id/nonrecordable/NonRecordableNode.java b/src/alias/id/nonrecordable/NonRecordableNode.java deleted file mode 100644 index 81f756bb..00000000 --- a/src/alias/id/nonrecordable/NonRecordableNode.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.nonrecordable; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasID; -import alias.id.AliasIDNode; - -public class NonRecordableNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public NonRecordableNode( PlaylistManager playlistManager, NonRecordable id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - @Override - public String getIconPath() - { - return "images/no_record.png"; - } - - public String toString() - { - return "Non-Recordable"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( (AliasID)getUserObject() ); - - save(); - - getModel().removeNodeFromParent( NonRecordableNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/priority/PriorityNode.java b/src/alias/id/priority/PriorityNode.java deleted file mode 100644 index 52b94cec..00000000 --- a/src/alias/id/priority/PriorityNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ -package alias.id.priority; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class PriorityNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public PriorityNode( PlaylistManager playlistManager, Priority priority ) - { - super( playlistManager, priority ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public Priority getPriority() - { - return (Priority)getUserObject(); - } - - public String toString() - { - return getPriority().toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getPriority() ); - - save(); - - getModel().removeNodeFromParent( PriorityNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/siteID/SiteIDNode.java b/src/alias/id/siteID/SiteIDNode.java deleted file mode 100644 index a234584c..00000000 --- a/src/alias/id/siteID/SiteIDNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.siteID; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class SiteIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public SiteIDNode( PlaylistManager playlistManager, SiteID id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public SiteID getSiteID() - { - return (SiteID)getUserObject(); - } - - public String toString() - { - return "Site " + getSiteID().getSite(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getSiteID() ); - - save(); - - getModel().removeNodeFromParent( SiteIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/status/StatusIDNode.java b/src/alias/id/status/StatusIDNode.java deleted file mode 100644 index 2c20df17..00000000 --- a/src/alias/id/status/StatusIDNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.status; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class StatusIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public StatusIDNode( PlaylistManager playlistManager, StatusID id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public StatusID getStatusID() - { - return (StatusID)getUserObject(); - } - - public String toString() - { - return "Status Code " + getStatusID().getStatus(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getStatusID() ); - - save(); - - getModel().removeNodeFromParent( StatusIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/talkgroup/TalkgroupIDNode.java b/src/alias/id/talkgroup/TalkgroupIDNode.java deleted file mode 100644 index a58ffb05..00000000 --- a/src/alias/id/talkgroup/TalkgroupIDNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.talkgroup; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class TalkgroupIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public TalkgroupIDNode( PlaylistManager playlistManager, TalkgroupID id ) - { - super( playlistManager,id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public TalkgroupID getTalkgroupID() - { - return (TalkgroupID)getUserObject(); - } - - public String toString() - { - return "Talkgroup " + getTalkgroupID().getTalkgroup(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getTalkgroupID() ); - - save(); - - getModel().removeNodeFromParent( TalkgroupIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/alias/id/uniqueID/UniqueIDNode.java b/src/alias/id/uniqueID/UniqueIDNode.java deleted file mode 100644 index 430ecdbd..00000000 --- a/src/alias/id/uniqueID/UniqueIDNode.java +++ /dev/null @@ -1,87 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package alias.id.uniqueID; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import alias.AliasNode; -import alias.id.AliasIDNode; - -public class UniqueIDNode extends AliasIDNode -{ - private static final long serialVersionUID = 1L; - - public UniqueIDNode( PlaylistManager playlistManager, UniqueID id ) - { - super( playlistManager, id ); - } - - @Override - public JPanel getEditor() - { - return null; - } - - public UniqueID getUniqueID() - { - return (UniqueID)getUserObject(); - } - - public String toString() - { - return "Unique ID " + getUniqueID().getUid(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Delete" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - int n = JOptionPane.showConfirmDialog( getModel().getTree(), - "Are you sure you want to permanently delete this node?" ); - - if( n == JOptionPane.YES_OPTION ) - { - AliasNode parent = (AliasNode)getParent(); - - parent.getAlias().removeAliasID( getUniqueID() ); - - save(); - - getModel().removeNodeFromParent( UniqueIDNode.this ); - } - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java index 22d3232c..c3f28c87 100644 --- a/src/controller/ConfigurationControllerModel.java +++ b/src/controller/ConfigurationControllerModel.java @@ -18,7 +18,6 @@ package controller; import java.util.ArrayList; -import java.util.Enumeration; import java.util.Iterator; import java.util.List; @@ -29,21 +28,14 @@ import javax.swing.tree.MutableTreeNode; import javax.swing.tree.TreePath; import module.decode.DecoderType; - -import playlist.PlaylistManager; -import playlist.PlaylistNode; - import settings.SettingsManager; import source.SourceManager; -import source.recording.RecordingGroupNode; import source.tuner.Tuner; import source.tuner.TunerGroupNode; import source.tuner.TunerNode; import source.tuner.TunerSelectionListener; import controller.channel.Channel; import controller.channel.ChannelModel; -import controller.channel.ChannelNode; - import controller.channel.ChannelProcessingManager; public class ConfigurationControllerModel extends DefaultTreeModel @@ -55,13 +47,10 @@ public class ConfigurationControllerModel extends DefaultTreeModel private ArrayList mTunerSelectionListeners = new ArrayList(); - private PlaylistNode mPlaylistNode; private TunerGroupNode mTunerGroupNode; -// private RecordingGroupNode mRecordingGroupNode; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; - private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; private SourceManager mSourceManager; @@ -73,7 +62,6 @@ public class ConfigurationControllerModel extends DefaultTreeModel */ public ConfigurationControllerModel( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, SettingsManager settingsManager, SourceManager sourceManager ) { @@ -81,7 +69,6 @@ public class ConfigurationControllerModel extends DefaultTreeModel mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; mSettingsManager = settingsManager; mSourceManager = sourceManager; @@ -117,13 +104,13 @@ public class ConfigurationControllerModel extends DefaultTreeModel // insertNodeInto( mRecordingGroupNode, (MutableTreeNode)root, 1 ); // mRecordingGroupNode.loadRecordings(); - /** - * Add the playlist node - */ - mPlaylistNode = new PlaylistNode( mChannelModel, mChannelProcessingManager, - mPlaylistManager, mSettingsManager, mSourceManager ); - insertNodeInto( mPlaylistNode, (MutableTreeNode)root, 1 ); - mPlaylistNode.loadPlaylist(); +// /** +// * Add the playlist node +// */ +// mPlaylistNode = new PlaylistNode( mChannelModel, mChannelProcessingManager, +// mPlaylistManager, mSettingsManager, mSourceManager ); +// insertNodeInto( mPlaylistNode, (MutableTreeNode)root, 1 ); +// mPlaylistNode.loadPlaylist(); } public void showNode( DefaultMutableTreeNode node ) @@ -146,8 +133,7 @@ public class ConfigurationControllerModel extends DefaultTreeModel for( Tuner tuner: tuners ) { TunerNode child = new TunerNode( mChannelModel, - mChannelProcessingManager, mPlaylistManager, mSettingsManager, - tuner ); + mChannelProcessingManager, mSettingsManager, tuner ); insertNodeInto( child, parent, parent.getChildCount() ); @@ -250,28 +236,11 @@ public class ConfigurationControllerModel extends DefaultTreeModel public ArrayList getCurrentChannelConfigs() { - return getChannelConfigs( mPlaylistNode ); + return null; } private ArrayList getChannelConfigs( DefaultMutableTreeNode parent ) { - ArrayList configs = new ArrayList(); - - Enumeration nodes = parent.children(); - - while( nodes.hasMoreElements() ) - { - DefaultMutableTreeNode node = nodes.nextElement(); - - if( node instanceof ChannelNode ) - { - configs.add( ((ChannelNode)node).getChannel() ); - } - - //Recursive call - configs.addAll( getChannelConfigs( node ) ); - } - - return configs; + return null; } } diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index 8c58d145..c9018a7c 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -33,7 +33,6 @@ import module.decode.event.CallEventPanel; import module.decode.event.MessageActivityPanel; import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; -import playlist.PlaylistManager; import settings.SettingsManager; import source.SourceManager; import spectrum.ChannelSpectrumPanel; @@ -48,6 +47,7 @@ import com.jidesoft.swing.JideTabbedPane; import controller.channel.ChannelController; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; +import controller.channel.map.ChannelMapModel; public class ControllerPanel extends JPanel { @@ -83,9 +83,9 @@ public class ControllerPanel extends JPanel ConfigurationControllerModel controller, AliasModel aliasModel, ChannelModel channelModel, + ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager, MapService mapService, - PlaylistManager playlistManager, SettingsManager settingsManager, SourceManager sourceManager ) { @@ -105,9 +105,10 @@ public class ControllerPanel extends JPanel channelProcessingManager ); mChannelStateList = new ChannelList( channelModel, channelProcessingManager, - playlistManager, mSettingsManager ); + mSettingsManager ); - mChannelController = new ChannelController( channelModel, playlistManager, sourceManager ); + mChannelController = new ChannelController( channelModel, channelMapModel, + sourceManager, aliasModel ); mAliasController = new AliasController( aliasModel, mSettingsManager ); diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java index 498fae64..c571adf7 100644 --- a/src/controller/channel/ChannelController.java +++ b/src/controller/channel/ChannelController.java @@ -25,9 +25,12 @@ import org.slf4j.LoggerFactory; import playlist.PlaylistManager; import source.SourceManager; +import alias.AliasModel; import com.jidesoft.swing.JideSplitPane; +import controller.channel.map.ChannelMapModel; + public class ChannelController extends JPanel implements ActionListener, ListSelectionListener { @@ -47,13 +50,15 @@ public class ChannelController extends JPanel private JButton mCopyChannelButton = new JButton( COPY_CHANNEL ); private JButton mDeleteChannelButton = new JButton( DELETE_CHANNEL ); - public ChannelController( ChannelModel channelModel, - PlaylistManager playlistManager, - SourceManager sourceManager ) + public ChannelController( ChannelModel channelModel, + ChannelMapModel channelMapModel, + SourceManager sourceManager, + AliasModel aliasModel ) { mChannelModel = channelModel; - mEditor = new ChannelEditor( channelModel, playlistManager, sourceManager ); + mEditor = new ChannelEditor( channelModel, channelMapModel, + sourceManager, aliasModel ); mChannelModel.addListener( mEditor ); init(); diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 8836a2da..79779120 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -40,10 +40,12 @@ import playlist.PlaylistManager; import record.RecordComponentEditor; import source.SourceComponentEditor; import source.SourceManager; +import alias.AliasModel; import com.jidesoft.swing.JideTabbedPane; import controller.channel.ChannelEvent.Event; +import controller.channel.map.ChannelMapModel; public class ChannelEditor extends JPanel implements ActionListener, ChannelEventListener @@ -65,23 +67,24 @@ public class ChannelEditor extends JPanel private JLabel mChannelName = new JLabel( "Channel:" ); private ChannelModel mChannelModel; - private PlaylistManager mPlaylistManager; + private ChannelMapModel mChannelMapModel; private SourceManager mSourceManager; private boolean mChannelEnableRequested = false; public ChannelEditor( ChannelModel channelModel, - PlaylistManager playlistManager, - SourceManager sourceManager ) + ChannelMapModel channelMapModel, + SourceManager sourceManager, + AliasModel aliasModel ) { super(); mChannelModel = channelModel; - mPlaylistManager = playlistManager; + mChannelMapModel = channelMapModel; mSourceManager = sourceManager; - mNameConfigurationEditor = new NameConfigurationEditor( - mChannelModel, mPlaylistManager ); + mNameConfigurationEditor = new NameConfigurationEditor( aliasModel, + mChannelModel ); initGUI(); } @@ -137,7 +140,7 @@ public class ChannelEditor extends JPanel mSourceEditor = new SourceComponentEditor( mSourceManager ); tabs.addTab( "Source", mSourceEditor ); - mDecodeEditor = new DecodeComponentEditor( mPlaylistManager ); + mDecodeEditor = new DecodeComponentEditor( mChannelMapModel ); tabs.addTab( "Decoder", mDecodeEditor ); mAuxDecodeEditor = new AuxDecodeComponentEditor(); diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java deleted file mode 100644 index ca8e5b37..00000000 --- a/src/controller/channel/ChannelNode.java +++ /dev/null @@ -1,190 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.channel; - -import java.awt.Color; -import java.text.DecimalFormat; - -import javax.swing.JMenu; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; -import source.SourceManager; -import source.tuner.TunerChannel; -import controller.ConfigurableNode; -import controller.site.SiteNode; -import controller.system.SystemNode; - -/** - * A channel has all of the pieces needed to wire together a source, decoder, - * event logger and recorder and be started and stopped. - */ -public class ChannelNode extends ConfigurableNode implements ChannelEventListener -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = LoggerFactory.getLogger( ChannelNode.class ); - - private static DecimalFormat sFORMAT = new DecimalFormat( "0.0000" ); - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SourceManager mSourceManager; - - public ChannelNode( Channel channel, - ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SourceManager sourceManager ) - { - super( playlistManager, channel ); - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSourceManager = sourceManager; - } - - public void init() - { - /* Set the owning system and site names for the channel */ - SiteNode siteNode = (SiteNode)getParent(); - - if( siteNode != null ) - { - getChannel().setSite( siteNode.getSite().getName() ); - - SystemNode systemNode = (SystemNode)siteNode.getParent(); - - if( systemNode != null ) - { - getChannel().setSystem( systemNode.getSystem().getName() ); - } - } - - /* Add this node as listener to receive changes from underlying channel */ -// getChannel().addListener( this ); - - } - - /** - * Listener method to receive channel changes from the underlying chnannel - */ - @Override - public void channelChanged( ChannelEvent event ) - { - switch( event.getEvent() ) - { - /* Refresh the node for each of these events */ - case NOTIFICATION_SELECTION_CHANGE: - case NOTIFICATION_PROCESSING_START: - case NOTIFICATION_PROCESSING_STOP: - this.refresh(); - break; - /* We're being deleted, so cleanup */ - case NOTIFICATION_DELETE: - delete(); - break; - default: - break; - } - } - - @Override - public String getIconPath() - { - return getChannel().getDecodeConfiguration().getDecoderType() - .getIconFilename(); - } - - @Override - public Color getBackgroundColor() - { - if( getChannel().getEnabled() ) - { - if( getChannel().getEnabled() ) - { - return Color.GREEN; - } - else - { - return Color.RED; - } - } - - return null; - } - - @Override - public JPanel getEditor() - { - return new OldChannelEditor( getChannel(), mChannelModel, getPlaylistManager(), mSourceManager ); - } - - public Channel getChannel() - { - return (Channel)this.getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getChannel().getName() ); - - TunerChannel channel = getChannel().getTunerChannel(); - - if( channel != null ) - { - sb.append( " (" ); - sb.append( sFORMAT.format( (double)channel.getFrequency() / 1000000.0d ) + " MHz" ); - sb.append( ")" ); - - } - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu popupMenu = new JPopupMenu(); - - JMenu channelMenu = ChannelUtils.getContextMenu( mChannelModel, - mChannelProcessingManager, getPlaylistManager(), getChannel(), - getModel().getTree() ); - - if( channelMenu != null ) - { - popupMenu.add( channelMenu ); - } - - return popupMenu; - } - - public void delete() - { - ((SiteNode)getParent()).getSite().removeChannel( getChannel() ); - - save(); - - getModel().removeNodeFromParent( ChannelNode.this ); - } -} diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index dac06b4d..52ab21a2 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -40,12 +40,14 @@ import sample.Listener; import source.Source; import source.SourceException; import source.SourceManager; +import alias.AliasModel; import audio.AudioPacket; import audio.metadata.Metadata; import audio.metadata.MetadataType; import controller.ThreadPoolManager; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent.Event; +import controller.channel.map.ChannelMapModel; import filter.FilterSet; public class ChannelProcessingManager implements ChannelEventListener @@ -58,22 +60,25 @@ public class ChannelProcessingManager implements ChannelEventListener private List> mMessageListeners = new CopyOnWriteArrayList<>(); private ChannelModel mChannelModel; + private ChannelMapModel mChannelMapModel; + private AliasModel mAliasModel; private EventLogManager mEventLogManager; - private PlaylistManager mPlaylistManager; private RecorderManager mRecorderManager; private SourceManager mSourceManager; private ThreadPoolManager mThreadPoolManager; public ChannelProcessingManager( ChannelModel channelModel, + ChannelMapModel channelMapModel, + AliasModel aliasModel, EventLogManager eventLogManager, - PlaylistManager playlistManager, RecorderManager recorderManager, SourceManager sourceManager, ThreadPoolManager threadPoolManager ) { mChannelModel = channelModel; + mChannelMapModel = channelMapModel; + mAliasModel = aliasModel; mEventLogManager = eventLogManager; - mPlaylistManager = playlistManager; mRecorderManager = recorderManager; mSourceManager = sourceManager; mThreadPoolManager = threadPoolManager; @@ -201,7 +206,7 @@ public class ChannelProcessingManager implements ChannelEventListener /* Processing Modules */ List modules = DecoderFactory.getModules( mChannelModel, - this, mPlaylistManager, channel ); + mChannelMapModel, this, mAliasModel, channel ); processingChain.addModules( modules ); /* Setup message activity model with filtering */ diff --git a/src/controller/channel/ChannelUtils.java b/src/controller/channel/ChannelUtils.java index 9a716ea3..6434eb4c 100644 --- a/src/controller/channel/ChannelUtils.java +++ b/src/controller/channel/ChannelUtils.java @@ -9,26 +9,20 @@ import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JSeparator; -import controller.channel.ChannelEvent.Event; import module.ProcessingChain; import module.decode.event.ActivitySummaryFrame; import module.decode.state.DecoderState; -import playlist.PlaylistManager; +import controller.channel.ChannelEvent.Event; public class ChannelUtils { - private static final boolean ENABLED = true; - private static final boolean DISABLED = false; - private static final boolean BROADCAST_CHANGE = true; - /** * Creates a context menu for the channel argument */ public static JMenu getContextMenu( final ChannelModel channelModel, - final ChannelProcessingManager channelProcessingManager, - final PlaylistManager playlistManager, - final Channel channel, - final Component anchor ) + final ChannelProcessingManager channelProcessingManager, + final Channel channel, + final Component anchor ) { if( channel != null ) { diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java index 3101e12e..f9b65bce 100644 --- a/src/controller/channel/NameConfigurationEditor.java +++ b/src/controller/channel/NameConfigurationEditor.java @@ -27,12 +27,11 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JTextField; import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; -import alias.AliasList; +import alias.AliasModel; public class NameConfigurationEditor extends ChannelConfigurationEditor { @@ -42,21 +41,20 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); + private AliasModel mAliasModel; private ChannelModel mChannelModel; - private PlaylistManager mPlaylistManager; private JTextField mChannelName = new JTextField( DEFAULT_NAME ); private JComboBox mSystemNameCombo = new JComboBox<>( EMPTY_MODEL ); private JComboBox mSiteNameCombo = new JComboBox<>( EMPTY_MODEL ); - private JComboBox mAliasListCombo; + private JComboBox mAliasListCombo; private Channel mChannel; - public NameConfigurationEditor( ChannelModel model, - PlaylistManager playlistManager ) + public NameConfigurationEditor( AliasModel aliasModel, ChannelModel model ) { + mAliasModel = aliasModel; mChannelModel = model; - mPlaylistManager = playlistManager; setLayout( new MigLayout( "fill,wrap 2", "[right][]", "[][][][][][grow]" ) ); @@ -110,15 +108,14 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor */ add( new JLabel( "Alias List:" ) ); - mAliasListCombo = new JComboBox(); + mAliasListCombo = new JComboBox(); - List lists = mPlaylistManager.getPlayist() - .getAliasDirectory().getAliasList(); + List lists = mAliasModel.getListNames(); Collections.sort( lists ); - mAliasListCombo.setModel( new DefaultComboBoxModel( - lists.toArray( new AliasList[ lists.size() ] ) ) ); + mAliasListCombo.setModel( new DefaultComboBoxModel( + lists.toArray( new String[ lists.size() ] ) ) ); add( mAliasListCombo, "growx" ); @@ -126,35 +123,35 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor final JButton aliasListButton = new JButton( "New Alias List ..." ); - aliasListButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - String name = JOptionPane.showInputDialog( aliasListButton, - "Please enter an Alias List name:" ); - - if( name != null && !name.isEmpty() ) - { - AliasList aliasList = new AliasList( name ); - - mPlaylistManager.getPlayist().getAliasDirectory() - .addAliasList( aliasList ); - - mPlaylistManager.save(); - - List lists = mPlaylistManager.getPlayist() - .getAliasDirectory().getAliasList(); - - Collections.sort( lists ); - - mAliasListCombo.setModel( new DefaultComboBoxModel( - lists.toArray( new AliasList[ lists.size() ] ) ) ); - - mAliasListCombo.setSelectedItem( aliasList ); - } - } - } ); +// aliasListButton.addActionListener( new ActionListener() +// { +// @Override +// public void actionPerformed( ActionEvent e ) +// { +// String name = JOptionPane.showInputDialog( aliasListButton, +// "Please enter an Alias List name:" ); +// +// if( name != null && !name.isEmpty() ) +// { +// AliasListOld aliasList = new AliasListOld( name ); +// +// mPlaylistManager.getPlayist().getAliasDirectory() +// .addAliasList( aliasList ); +// +// mPlaylistManager.save(); +// +// List lists = mPlaylistManager.getPlayist() +// .getAliasDirectory().getAliasList(); +// +// Collections.sort( lists ); +// +// mAliasListCombo.setModel( new DefaultComboBoxModel( +// lists.toArray( new AliasListOld[ lists.size() ] ) ) ); +// +// mAliasListCombo.setSelectedItem( aliasList ); +// } +// } +// } ); add( aliasListButton, "growx" ); } @@ -173,8 +170,7 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor mChannel.setSite( ( site == null ? null : (String)site ) ); Object aliasList = mAliasListCombo.getSelectedItem(); - mChannel.setAliasListName( aliasList == null ? null : - ((AliasList)aliasList).getName() ); + mChannel.setAliasListName( aliasList == null ? null : (String)aliasList ); } } @@ -232,10 +228,7 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor if( aliasListName != null ) { - AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mAliasListCombo.setSelectedItem( selected ); + mAliasListCombo.setSelectedItem( aliasListName ); } } else diff --git a/src/controller/channel/OldChannelEditor.java b/src/controller/channel/OldChannelEditor.java deleted file mode 100644 index 6882e457..00000000 --- a/src/controller/channel/OldChannelEditor.java +++ /dev/null @@ -1,260 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.channel; - - -import java.awt.Color; -import java.awt.ComponentOrientation; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Collections; -import java.util.List; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JTextField; - -import module.decode.AuxDecodeComponentEditor; -import module.decode.DecodeComponentEditor; -import module.log.EventLogComponentEditor; -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; -import record.RecordComponentEditor; -import source.SourceComponentEditor; -import source.SourceManager; -import alias.AliasList; - -import com.jidesoft.swing.JideTabbedPane; - -public class OldChannelEditor extends ChannelConfigurationEditor implements ActionListener -{ - private final static Logger mLog = LoggerFactory.getLogger( OldChannelEditor.class ); - - private static final long serialVersionUID = 1L; - - private DecodeComponentEditor mDecodeEditor; - private AuxDecodeComponentEditor mAuxDecodeEditor; - private EventLogComponentEditor mEventLogEditor; - private RecordComponentEditor mRecordEditor; - private SourceComponentEditor mSourceEditor; - - private JTextField mChannelName; - private JCheckBox mChannelEnabled = new JCheckBox(); - private JComboBox mComboAliasLists; - - private Channel mChannel; - private ChannelModel mChannelModel; - private PlaylistManager mPlaylistManager; - private SourceManager mSourceManager; - - public OldChannelEditor( Channel channel, - ChannelModel channelModel, - PlaylistManager playlistManager, - SourceManager sourceManager ) - { - super(); - - mChannel = channel; - mChannelModel = channelModel; - mPlaylistManager = playlistManager; - mSourceManager = sourceManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow][]" ) ); - - add( new JLabel( "Channel Configuration" ), "span, align center" ); - - add( new JLabel( "Enabled:" ) ); - - mChannelEnabled.setSelected( mChannel.getEnabled() ); - mChannelEnabled.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - add( mChannelEnabled ); - - add( new JLabel( "Name:" ) ); - - mChannelName = new JTextField( mChannel.getName() ); - add( mChannelName, "growx,push" ); - - /** - * ComboBox: Alias Lists - */ - add( new JLabel( "Alias List:" ) ); - - mComboAliasLists = new JComboBox(); - - List lists = mPlaylistManager.getPlayist() - .getAliasDirectory().getAliasList(); - - Collections.sort( lists ); - - mComboAliasLists.setModel( new DefaultComboBoxModel( - lists.toArray( new AliasList[ lists.size() ] ) ) ); - - String aliasListName = mChannel.getAliasListName(); - - if( aliasListName != null ) - { - AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() - .getAliasList( aliasListName ); - - mComboAliasLists.setSelectedItem( selected ); - } - - add( mComboAliasLists, "growx,push" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - mSourceEditor = new SourceComponentEditor( mSourceManager ); - tabs.addTab( "Source", mSourceEditor ); - - mDecodeEditor = new DecodeComponentEditor( mPlaylistManager ); - tabs.addTab( "Decoder", mDecodeEditor ); - - mAuxDecodeEditor = new AuxDecodeComponentEditor(); - tabs.addTab( "Aux", mAuxDecodeEditor ); - - mEventLogEditor = new EventLogComponentEditor(); - tabs.addTab( "Event Log", mEventLogEditor ); - - mRecordEditor = new RecordComponentEditor(); - tabs.addTab( "Record", mRecordEditor ); - - add( tabs, "span,grow,push" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( OldChannelEditor.this ); - add( btnSave, "growx,push" ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( OldChannelEditor.this ); - add( btnReset, "growx,push" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - save(); - } - else if( command.contentEquals( "Reset" ) ) - { -// reset(); - } - } - - @Override - public void save() - { -// try -// { -// /** -// * Validate the source configuration against the other component -// * configuration editors -// */ -// validate( mSourceEditor.getSourceEditor() ); -// -// mSourceEditor.save(); -// mDecodeEditor.save(); -// mAuxDecodeEditor.save(); -// mEventLogEditor.save(); -// mRecordEditor.save(); -// -// mChannel.setName( mChannelName.getText() ); -// mChannel.setEnabled( mChannelEnabled.isSelected() ); -// -// AliasList selected = mComboAliasLists.getItemAt( -// mComboAliasLists.getSelectedIndex() ); -// if( selected != null ) -// { -// mChannel.setAliasListName( selected.getName() ); -// } -// } -// catch ( ConfigurationValidationException validationException ) -// { -// /* ChannelValidationException can be thrown by any of the component -// * editors. Show the validation error text in a popup menu to the -// * user */ -// JOptionPane.showMessageDialog( OldChannelEditor.this, -// validationException.getMessage(), -// "Channel Configuration Error", -// JOptionPane.ERROR_MESSAGE ); -// } - } - -// @Override -// public void reset() -// { -// mChannelName.setText( mChannel.getName() ); -// mChannelName.requestFocus(); -// mChannelName.requestFocusInWindow(); -// -// mChannelEnabled.setSelected( mChannel.getEnabled() ); -// mChannelEnabled.requestFocus(); -// mChannelEnabled.requestFocusInWindow(); -// -// String aliasListName = mChannel.getAliasListName(); -// -// if( aliasListName != null ) -// { -// AliasList selected = mPlaylistManager.getPlayist().getAliasDirectory() -// .getAliasList( aliasListName ); -// -// mComboAliasLists.setSelectedItem( selected ); -// } -// -// -// mSourceEditor.reset(); -// mDecodeEditor.reset(); -// mAuxDecodeEditor.reset(); -// mEventLogEditor.reset(); -// mRecordEditor.reset(); -// } - - @Override - public void setConfiguration( Channel channel ) - { - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - mSourceEditor.validate(); - mDecodeEditor.validate(); - mAuxDecodeEditor.validate(); - mEventLogEditor.validate(); - mRecordEditor.validate(); - } - -} diff --git a/src/controller/channel/map/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java index 11ede472..85e39357 100644 --- a/src/controller/channel/map/ChannelMapEditor.java +++ b/src/controller/channel/map/ChannelMapEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,22 +17,22 @@ ******************************************************************************/ package controller.channel.map; +import gui.editor.DocumentListenerEditor; + import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import javax.swing.AbstractCellEditor; import javax.swing.JButton; import javax.swing.JFormattedTextField; import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.TableCellEditor; import javax.swing.text.InternationalFormatter; @@ -41,28 +41,45 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class ChannelMapEditor extends JPanel implements ActionListener +public class ChannelMapEditor extends DocumentListenerEditor + implements ListSelectionListener { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( ChannelMapEditor.class ); - private ChannelMapNode mChannelMapNode; - private JLabel mNameLabel; private JTextField mNameText; - private ChannelMapModel mRangeModel; + private ChannelRangeModel mRangeModel; private JTable mRangeTable; private JButton mDelete; - private ChannelMap mChannelMapCopy; - public ChannelMapEditor( ChannelMapNode ChannelMap ) + public ChannelMapEditor() { - mChannelMapNode = ChannelMap; - mChannelMapCopy = mChannelMapNode.getChannelMap().copyOf(); initGUI(); } + private ChannelMap getChannelMap() + { + if( hasItem() ) + { + return (ChannelMap)getItem(); + } + + return null; + } + + @Override + public void setItem( ChannelMap item ) + { + super.setItem( item ); + + if( hasItem() ) + { + + } + } + private void initGUI() { setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow][][]" ) ); @@ -72,20 +89,11 @@ public class ChannelMapEditor extends JPanel implements ActionListener mNameLabel = new JLabel( "Name:" ); add( mNameLabel ); - mNameText = new JTextField( mChannelMapCopy.getName() ); - mNameText.addFocusListener( new FocusListener() - { - @Override public void focusGained( FocusEvent arg0 ) {} - - @Override - public void focusLost( FocusEvent arg0 ) - { - mChannelMapCopy.setName( mNameText.getText() ); - } - } ); + mNameText = new JTextField(); + mNameText.getDocument().addDocumentListener( this ); add( mNameText, "growx, push" ); - mRangeModel = new ChannelMapModel( mChannelMapCopy ); + mRangeModel = new ChannelRangeModel(); mRangeTable = new JTable( mRangeModel ); mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) ); @@ -101,88 +109,89 @@ public class ChannelMapEditor extends JPanel implements ActionListener add( scrollPane, "span,grow,push"); JButton btnNew = new JButton( "New Range" ); - btnNew.addActionListener( ChannelMapEditor.this ); +// btnNew.addActionListener( ChannelMapEditor.this ); add( btnNew ); mDelete = new JButton( "Delete" ); - mDelete.addActionListener( ChannelMapEditor.this ); +// mDelete.addActionListener( ChannelMapEditor.this ); add( mDelete, "wrap" ); JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( ChannelMapEditor.this ); +// btnSave.addActionListener( ChannelMapEditor.this ); add( btnSave, "growx" ); JButton btnReset = new JButton( "Reset " ); - btnReset.addActionListener( ChannelMapEditor.this ); +// btnReset.addActionListener( ChannelMapEditor.this ); add( btnReset, "wrap" ); } private boolean checkForValidRanges() { - int invalidRow = mChannelMapCopy.getInvalidRange(); - - if( invalidRow != -1 && invalidRow <= mRangeTable.getRowCount() ) - { - mRangeTable.setRowSelectionInterval( invalidRow, invalidRow ); - - JOptionPane.showMessageDialog( this, - "Starting channel number must be less than ending channel number", - "Channel number range error", - JOptionPane.WARNING_MESSAGE ); - - return false; - } + mLog.error( "Fix Me!" ); +// int invalidRow = mChannelMapCopy.getInvalidRange(); +// +// if( invalidRow != -1 && invalidRow <= mRangeTable.getRowCount() ) +// { +// mRangeTable.setRowSelectionInterval( invalidRow, invalidRow ); +// +// JOptionPane.showMessageDialog( this, +// "Starting channel number must be less than ending channel number", +// "Channel number range error", +// JOptionPane.WARNING_MESSAGE ); +// +// return false; +// } return true; } - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - switch( command ) - { - case "New Range": - mRangeModel.addRange( new ChannelRange() ); - break; - case "Save": - if( checkForValidRanges() ) - { - mChannelMapNode.getChannelMap() - .setName( mChannelMapCopy.getName() ); - mChannelMapNode.getChannelMap() - .setRanges( mChannelMapCopy.getRanges() ); - - mChannelMapNode.save(); - - mChannelMapNode.refresh(); - - mChannelMapNode.show(); - } - case "Reset": - mChannelMapCopy = mChannelMapNode.getChannelMap(); - mRangeModel.setChannelMap( mChannelMapCopy ); - mNameText.setText( mChannelMapCopy.getName() ); - break; - case "Delete": - int row = mRangeTable.getSelectedRow(); - - if( row < 0 ) - { - JOptionPane.showMessageDialog( this, - "Please select a channel range to delete", - "Select a range", - JOptionPane.WARNING_MESSAGE ); - } - else - { - mRangeModel.removeRange( - mRangeTable.convertRowIndexToModel( row ) ); - } - break; - } - } +// @Override +// public void actionPerformed( ActionEvent e ) +// { +// String command = e.getActionCommand(); +// +// switch( command ) +// { +// case "New Range": +// mRangeModel.addRange( new ChannelRange() ); +// break; +// case "Save": +// if( checkForValidRanges() ) +// { +// mChannelMapNode.getChannelMap() +// .setName( mChannelMapCopy.getName() ); +// mChannelMapNode.getChannelMap() +// .setRanges( mChannelMapCopy.getRanges() ); +// +// mChannelMapNode.save(); +// +// mChannelMapNode.refresh(); +// +// mChannelMapNode.show(); +// } +// case "Reset": +// mChannelMapCopy = mChannelMapNode.getChannelMap(); +// mRangeModel.setChannelMap( mChannelMapCopy ); +// mNameText.setText( mChannelMapCopy.getName() ); +// break; +// case "Delete": +// int row = mRangeTable.getSelectedRow(); +// +// if( row < 0 ) +// { +// JOptionPane.showMessageDialog( this, +// "Please select a channel range to delete", +// "Select a range", +// JOptionPane.WARNING_MESSAGE ); +// } +// else +// { +// mRangeModel.removeRange( +// mRangeTable.convertRowIndexToModel( row ) ); +// } +// break; +// } +// } public class RangeTableEditor extends AbstractCellEditor implements TableCellEditor, ActionListener @@ -221,4 +230,17 @@ public class ChannelMapEditor extends JPanel implements ActionListener return mEditor; } } + + @Override + public void save() + { + // TODO Auto-generated method stub + + } + + @Override + public void valueChanged( ListSelectionEvent e ) + { + mLog.debug( "List Select Event from: " + e.getSource().getClass() ); + } } diff --git a/src/controller/channel/map/ChannelMapEvent.java b/src/controller/channel/map/ChannelMapEvent.java new file mode 100644 index 00000000..a3ca8821 --- /dev/null +++ b/src/controller/channel/map/ChannelMapEvent.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package controller.channel.map; + +public class ChannelMapEvent +{ + private ChannelMap mChannelMap; + private Event mEvent; + private String mPreviousChannelMapName; + + /** + * ChannelMapEvent - event describing any changes to a channel map + * + * @param channelMap that changed + * @param event describing the change + */ + public ChannelMapEvent( ChannelMap channelMap, Event event ) + { + mChannelMap = channelMap; + mEvent = event; + } + + /** + * ChannelMapEvent - channel map renaming event. + * + * @param channelMap that changed + * @param previousName of the channel map + */ + public ChannelMapEvent( ChannelMap channelMap, String previousName ) + { + this( channelMap, Event.RENAME ); + + mPreviousChannelMapName = previousName; + } + + public ChannelMap getChannelMap() + { + return mChannelMap; + } + + public String getPreviousChannelMapName() + { + return mPreviousChannelMapName; + } + + public Event getEvent() + { + return mEvent; + } + + /** + * Events + */ + public enum Event + { + ADD, + CHANGE, + RENAME, + DELETE; + } +} diff --git a/src/controller/channel/map/ChannelMapListNode.java b/src/controller/channel/map/ChannelMapListNode.java deleted file mode 100644 index 9259bba3..00000000 --- a/src/controller/channel/map/ChannelMapListNode.java +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.channel.map; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import controller.ConfigurableNode; - -public class ChannelMapListNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public ChannelMapListNode( PlaylistManager playlistManager, ChannelMapList list ) - { - super( playlistManager, list ); - } - - public ChannelMapList getChannelMapList() - { - return (ChannelMapList)getUserObject(); - } - - public void init() - { - for( ChannelMap channelMap: getChannelMapList().getChannelMap() ) - { - ChannelMapNode node = new ChannelMapNode( getPlaylistManager(), channelMap ); - - getModel().addNode( node, ChannelMapListNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return "Channel Maps [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Add Channel Map" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - ChannelMap channelMap = new ChannelMap(); - - getChannelMapList().addChannelMap( channelMap ); - - getModel().addNode( new ChannelMapNode( getPlaylistManager(), channelMap ), - ChannelMapListNode.this, - ChannelMapListNode.this.getChildCount() ); - - save(); - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/controller/channel/map/ChannelMapManager.java b/src/controller/channel/map/ChannelMapManager.java new file mode 100644 index 00000000..d92626a5 --- /dev/null +++ b/src/controller/channel/map/ChannelMapManager.java @@ -0,0 +1,47 @@ +package controller.channel.map; + +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; + +import net.miginfocom.swing.MigLayout; + +import com.jidesoft.swing.JideSplitPane; + +public class ChannelMapManager extends JPanel +{ + private static final long serialVersionUID = 1L; + + private ChannelMapModel mChannelMapModel; + private JList mChannelMapList; + private ChannelMapEditor mChannelMapEditor; + + public ChannelMapManager( ChannelMapModel channelMapModel ) + { + mChannelMapModel = channelMapModel; + mChannelMapList = new JList( mChannelMapModel ); + mChannelMapEditor = new ChannelMapEditor(); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "", "[fill,grow]", "[fill,grow]" ) ); + + mChannelMapList.addListSelectionListener( mChannelMapEditor ); + mChannelMapList.setLayoutOrientation( JList.VERTICAL ); + mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + JScrollPane listScroller = new JScrollPane( mChannelMapList ); + + JScrollPane editorScroller = new JScrollPane( mChannelMapEditor ); + + JideSplitPane splitPane = new JideSplitPane(); + splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT ); + splitPane.add( listScroller ); + splitPane.add( editorScroller ); + + add( splitPane ); + } +} diff --git a/src/alias/id/AliasIDNode.java b/src/controller/channel/map/ChannelMapManagerFrame.java similarity index 56% rename from src/alias/id/AliasIDNode.java rename to src/controller/channel/map/ChannelMapManagerFrame.java index adaf3b36..c3c48628 100644 --- a/src/alias/id/AliasIDNode.java +++ b/src/controller/channel/map/ChannelMapManagerFrame.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -15,22 +15,28 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias.id; +package controller.channel.map; -import playlist.PlaylistManager; -import controller.ConfigurableNode; +import javax.swing.JFrame; -public class AliasIDNode extends ConfigurableNode +import net.miginfocom.swing.MigLayout; + +public class ChannelMapManagerFrame extends JFrame { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - public AliasIDNode( PlaylistManager playlistManager, AliasID id ) + public ChannelMapManagerFrame( ChannelMapModel channelMapModel ) { - super( playlistManager, id ); + init( new ChannelMapManager( channelMapModel ) ); + } + + private void init( ChannelMapManager channelMapManager ) + { + setTitle( "Channel Map Manager" ); + setSize( 800, 400 ); + setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); + add( channelMapManager ); + setLocationRelativeTo( null ); } - - public void save() - { - getPlaylistManager().save(); - } } diff --git a/src/controller/channel/map/ChannelMapModel.java b/src/controller/channel/map/ChannelMapModel.java index 50da7dfe..e0b486d4 100644 --- a/src/controller/channel/map/ChannelMapModel.java +++ b/src/controller/channel/map/ChannelMapModel.java @@ -1,171 +1,155 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ package controller.channel.map; -import javax.swing.table.AbstractTableModel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import javax.swing.AbstractListModel; -public class ChannelMapModel extends AbstractTableModel +import controller.channel.map.ChannelMapEvent.Event; +import sample.Broadcaster; +import sample.Listener; + +public class ChannelMapModel extends AbstractListModel { - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( ChannelMapModel.class ); + private static final long serialVersionUID = 1L; + + private List mChannelMaps = new ArrayList<>(); + private Broadcaster mEventBroadcaster = new Broadcaster<>(); - private static final int sSTART = 0; - private static final int sSTOP = 1; - private static final int sBASE = 2; - private static final int sSIZE = 3; + public ChannelMapModel() + { + } - protected int[] mColumnWidths = { 110, 110, 110, -1 }; + /** + * Returns an unmodifiable list of channel maps currently in the model + */ + public List getChannelMaps() + { + return Collections.unmodifiableList( mChannelMaps ); + } - protected String[] mHeaders = new String[] { "Begin", - "End", - "Base", - "Size" }; - - private ChannelMap mChannelMap; - - public ChannelMapModel( ChannelMap map ) + /** + * Returns the channel map with a matching name or null + */ + public ChannelMap getChannelMap( String name ) { - mChannelMap = map; - } - - public ChannelMap getChannelMap() - { - return mChannelMap; - } - - public void setChannelMap( ChannelMap map ) - { - mChannelMap = map; - - fireTableDataChanged(); - } - - public void addRange( ChannelRange range ) - { - mChannelMap.addRange( range ); - - int index = mChannelMap.getRanges().size() - 1; - - fireTableRowsInserted( index, index ); - } - - public void removeRange( int rangeIndex ) - { - if( rangeIndex < mChannelMap.getRanges().size() ) + for( ChannelMap channelMap: mChannelMaps ) { - mChannelMap.getRanges().remove( rangeIndex ); - } - - fireTableRowsDeleted( rangeIndex, rangeIndex ); - } - - public int[] getColumnWidths() - { - return mColumnWidths; - } - - public void setColumnWidths( int[] widths ) - { - if( widths.length != 4 ) - { - throw new IllegalArgumentException( "ChannelMapRangeModel - " - + "column widths array should have 5 elements" ); - } - else - { - mColumnWidths = widths; - } - } - - @Override - public int getRowCount() - { - return mChannelMap.getRanges().size(); - } - - @Override - public int getColumnCount() - { - return mHeaders.length; - } - - public String getColumnName( int column ) - { - return mHeaders[ column ]; - } - - @Override - public Object getValueAt( int rowIndex, int columnIndex ) - { - ChannelRange range = mChannelMap.getRanges().get( rowIndex ); - - switch( columnIndex ) - { - case sSTART: - return range.getFirstChannelNumber(); - case sSTOP: - return range.getLastChannelNumber(); - case sBASE: - return range.getBase(); - case sSIZE: - return range.getSize(); + if( channelMap.getName().equalsIgnoreCase( name ) ) + { + return channelMap; + } } return null; - } - - public boolean isCellEditable( int row, int column ) - { - return true; } - - public void setValueAt( Object value, int row, int column ) + + /** + * Adds a listener to receive notifications when channel map updates occur + */ + public void addListener( Listener listener ) { - try + mEventBroadcaster.addListener( listener ); + } + + /** + * Removes the channel map update notification listener + */ + public void removeListener( Listener listener ) + { + mEventBroadcaster.removeListener( listener ); + } + + /** + * Broadcasts a channel map change. + * + * Note: use the add/remove methods to add or remove channel maps from this + * model. When using those methods, an add or delete event will automatically + * be generated. + */ + public void broadcast( ChannelMapEvent event ) + { + if( event.getEvent() == ChannelMapEvent.Event.CHANGE || + event.getEvent() == ChannelMapEvent.Event.RENAME ) { - int parsedValue = Integer.parseInt( (String)value ); + int index = mChannelMaps.indexOf( event.getChannelMap() ); - ChannelRange range = mChannelMap.getRanges().get( row ); - - switch( column ) + if( index >= 0 ) { - case sSTART: - range.setFirstChannelNumber( parsedValue ); - break; - case sSTOP: - range.setLastChannelNumber( parsedValue ); - break; - case sBASE: - range.setBase( parsedValue ); - break; - case sSIZE: - range.setSize( parsedValue ); - break; + fireContentsChanged( this, index, index ); } } - catch( Exception e ) + + mEventBroadcaster.broadcast( event ); + } + + /** + * Adds a list of channel maps to this model + */ + public void addChannelMaps( List channelMaps ) + { + for( ChannelMap channelMap: channelMaps ) { - mLog.error( "ChannelMapModel - couldn't parse integer value " - + "for [" + value + "]", e ); + addChannelMap( channelMap ); } } + + /** + * Adds the channel map to this model + */ + public void addChannelMap( ChannelMap channelMap ) + { + if( !mChannelMaps.contains( channelMap ) ) + { + mChannelMaps.add( channelMap ); + + int index = mChannelMaps.indexOf( channelMap ); + + fireIntervalAdded( this, index, index ); + + broadcast( new ChannelMapEvent( channelMap, Event.ADD ) ); + } + + } + + /** + * Removes the channel map from this model + */ + public void removeChannelMap( ChannelMap channelMap ) + { + if( mChannelMaps.contains( channelMap ) ) + { + int index = mChannelMaps.indexOf( channelMap ); + + mChannelMaps.remove( channelMap ); + + fireIntervalRemoved( this, index, index ); + + broadcast( new ChannelMapEvent( channelMap, Event.DELETE ) ); + } + } + + /** + * Size/Number of channel maps in this model + */ + @Override + public int getSize() + { + return mChannelMaps.size(); + } + + /** + * Returns the channel map at the specified index + */ + @Override + public ChannelMap getElementAt( int index ) + { + if( index <= mChannelMaps.size() ) + { + return mChannelMaps.get( index ); + } + + return null; + } } diff --git a/src/controller/channel/map/ChannelMapNode.java b/src/controller/channel/map/ChannelMapNode.java deleted file mode 100644 index ef6c6bfe..00000000 --- a/src/controller/channel/map/ChannelMapNode.java +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.channel.map; - - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import controller.ConfigurableNode; - -public class ChannelMapNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - public ChannelMapNode( PlaylistManager playlistManager, ChannelMap ChannelMap ) - { - super( playlistManager, ChannelMap ); - } - - public void init() - { - //For consistency, but not used - } - - @Override - public String getIconPath() - { - return "images/ChannelMap.png"; - } - - @Override - public JPanel getEditor() - { - return new ChannelMapEditor( this ); - } - - public ChannelMap getChannelMap() - { - return (ChannelMap)getUserObject(); - } - - public String toString() - { - return getChannelMap().getName(); - } - - public void delete() - { - ChannelMapListNode parent = (ChannelMapListNode)getParent(); - - parent.getChannelMapList().removeChannelMap( getChannelMap() ); - - save(); - - getModel().removeNodeFromParent( ChannelMapNode.this ); - - /* Show the first child, if one exists so that we don't have a - * collapsed list */ - if( parent.getChildCount() > 0 ) - { - ((ConfigurableNode)parent.getFirstChild()).show(); - } - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - delete(); - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/controller/channel/map/ChannelRange.java b/src/controller/channel/map/ChannelRange.java index a9814239..a57ee199 100644 --- a/src/controller/channel/map/ChannelRange.java +++ b/src/controller/channel/map/ChannelRange.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 diff --git a/src/controller/channel/map/ChannelRangeModel.java b/src/controller/channel/map/ChannelRangeModel.java new file mode 100644 index 00000000..9e13d345 --- /dev/null +++ b/src/controller/channel/map/ChannelRangeModel.java @@ -0,0 +1,181 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package controller.channel.map; + +import javax.swing.table.AbstractTableModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChannelRangeModel extends AbstractTableModel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = + LoggerFactory.getLogger( ChannelRangeModel.class ); + + private static final int START = 0; + private static final int STOP = 1; + private static final int BASE = 2; + private static final int SIZE = 3; + + protected int[] mColumnWidths = { 110, 110, 110, -1 }; + + protected String[] mHeaders = new String[] { "Begin", + "End", + "Base", + "Size" }; + + private ChannelMap mChannelMap; + + public ChannelRangeModel() + { + } + + public void setChannelMap( ChannelMap channelMap ) + { + mChannelMap = channelMap; + fireTableDataChanged(); + } + + public void addRange( ChannelRange range ) + { + if( mChannelMap != null ) + { + mChannelMap.addRange( range ); + + int index = mChannelMap.getRanges().size() - 1; + + fireTableRowsInserted( index, index ); + } + } + + public void removeRange( int rangeIndex ) + { + if( mChannelMap != null ) + { + if( rangeIndex < mChannelMap.getRanges().size() ) + { + mChannelMap.getRanges().remove( rangeIndex ); + } + + fireTableRowsDeleted( rangeIndex, rangeIndex ); + } + } + + public int[] getColumnWidths() + { + return mColumnWidths; + } + + public void setColumnWidths( int[] widths ) + { + if( widths.length != 4 ) + { + throw new IllegalArgumentException( "ChannelMapRangeModel - " + + "column widths array should have 5 elements" ); + } + else + { + mColumnWidths = widths; + } + } + + @Override + public int getRowCount() + { + if( mChannelMap != null ) + { + return mChannelMap.getRanges().size(); + } + + return 0; + } + + @Override + public int getColumnCount() + { + return mHeaders.length; + } + + public String getColumnName( int column ) + { + return mHeaders[ column ]; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + if( mChannelMap != null ) + { + ChannelRange range = mChannelMap.getRanges().get( rowIndex ); + + switch( columnIndex ) + { + case START: + return range.getFirstChannelNumber(); + case STOP: + return range.getLastChannelNumber(); + case BASE: + return range.getBase(); + case SIZE: + return range.getSize(); + } + } + + return null; + } + + public boolean isCellEditable( int row, int column ) + { + return mChannelMap != null; + } + + public void setValueAt( Object value, int row, int column ) + { + if( mChannelMap != null ) + { + try + { + int parsedValue = Integer.parseInt( (String)value ); + + ChannelRange range = mChannelMap.getRanges().get( row ); + + switch( column ) + { + case START: + range.setFirstChannelNumber( parsedValue ); + break; + case STOP: + range.setLastChannelNumber( parsedValue ); + break; + case BASE: + range.setBase( parsedValue ); + break; + case SIZE: + range.setSize( parsedValue ); + break; + } + } + catch( Exception e ) + { + mLog.error( "ChannelMapModel - couldn't parse integer value " + + "for [" + value + "]", e ); + } + } + } +} diff --git a/src/controller/site/SiteEditor.java b/src/controller/site/SiteEditor.java deleted file mode 100644 index 6008a78c..00000000 --- a/src/controller/site/SiteEditor.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.site; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; - -public class SiteEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private SiteNode mSiteNode; - - private JLabel mLabelName; - private JTextField mTextSite; - - public SiteEditor( SiteNode site ) - { - mSiteNode = site; - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout() ); - - setBorder( BorderFactory.createTitledBorder( "Site" ) ); - - mLabelName = new JLabel( "Name:" ); - add( mLabelName, "align right" ); - - mTextSite = new JTextField( mSiteNode.getSite().getName() ); - add( mTextSite, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( SiteEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( SiteEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - boolean expanded = mSiteNode.getModel().getTree() - .isExpanded( new TreePath( mSiteNode ) ); - - String name = mTextSite.getText(); - - if( name != null ) - { - mSiteNode.getSite().setName( name ); - mSiteNode.save(); - mSiteNode.show(); - - if( expanded ) - { - mSiteNode.getModel().getTree().expandPath( new TreePath( mSiteNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( SiteEditor.this, "Please enter a site name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextSite.setText( mSiteNode.getSite().getName() ); - } - } -} diff --git a/src/controller/site/SiteNode.java b/src/controller/site/SiteNode.java deleted file mode 100644 index 65ad140f..00000000 --- a/src/controller/site/SiteNode.java +++ /dev/null @@ -1,181 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.site; - - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Enumeration; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.tree.DefaultMutableTreeNode; - -import playlist.PlaylistManager; -import source.SourceManager; -import controller.ConfigurableNode; -import controller.channel.Channel; -import controller.channel.ChannelModel; -import controller.channel.ChannelNode; -import controller.channel.ChannelProcessingManager; -import controller.system.System; -import controller.system.SystemNode; - -public class SiteNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SourceManager mSourceManager; - - public SiteNode( Site site, - ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SourceManager sourceManager ) - { - super( playlistManager, site ); - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSourceManager = sourceManager; - } - - public void init() - { - for( Channel channel: getSite().getChannel() ) - { - ChannelNode node = new ChannelNode( channel, mChannelModel, - mChannelProcessingManager, getPlaylistManager(), mSourceManager ); - - getModel().insertNodeInto( node, SiteNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String getIconPath() - { - return "images/site.png"; - } - - @Override - public Color getBackgroundColor() - { - @SuppressWarnings( "unchecked" ) - Enumeration nodes = children(); - - while( nodes.hasMoreElements() ) - { - ChannelNode child = (ChannelNode)nodes.nextElement(); - - if( child.getBackgroundColor() != null ) - { - return Color.CYAN; - } - } - - return null; - } - - @Override - public JPanel getEditor() - { - return new SiteEditor( this ); - } - - public Site getSite() - { - return (Site)getUserObject(); - } - - public String toString() - { - return getSite().getName(); - } - - public void delete() - { - for( int x = 0; x < getChildCount(); x++ ) - { - ((ChannelNode)getChildAt( x )).delete(); - } - - ((SystemNode)getParent()).getSystem().removeSite( getSite() ); - - save(); - - getModel().removeNodeFromParent( SiteNode.this ); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addChannelItem = new JMenuItem( "Add Channel" ); - addChannelItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - System system = ((SystemNode)getParent()).getSystem(); - - Channel channel = new Channel(); - channel.setSystem( system.getName() ); - channel.setSite( getSite().getName() ); - - getSite().addChannel( channel ); - - ChannelNode node = new ChannelNode( channel, mChannelModel, - mChannelProcessingManager, getPlaylistManager(), mSourceManager ); - - getModel().addNode( node, - SiteNode.this, - SiteNode.this.getChildCount() ); - - sort(); - save(); - node.show(); - } - } ); - - retVal.add( addChannelItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - delete(); - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/controller/system/SystemEditor.java b/src/controller/system/SystemEditor.java deleted file mode 100644 index f9c4e8b4..00000000 --- a/src/controller/system/SystemEditor.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.system; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.BorderFactory; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; -import javax.swing.tree.TreePath; - -import net.miginfocom.swing.MigLayout; - -public class SystemEditor extends JPanel implements ActionListener -{ - private static final long serialVersionUID = 1L; - private SystemNode mSystemNode; - - private JLabel mLabelSystem; - private JTextField mTextSystem; - - public SystemEditor( SystemNode system ) - { - mSystemNode = system; - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout() ); - - setBorder( BorderFactory.createTitledBorder( "System" ) ); - - mLabelSystem = new JLabel( "Name:" ); - add( mLabelSystem, "align right" ); - - mTextSystem = new JTextField( mSystemNode.getSystem().getName() ); - add( mTextSystem, "grow, wrap" ); - - JButton btnSave = new JButton( "Save" ); - btnSave.addActionListener( SystemEditor.this ); - add( btnSave ); - - JButton btnReset = new JButton( "Reset" ); - btnReset.addActionListener( SystemEditor.this ); - add( btnReset, "wrap" ); - } - - @Override - public void actionPerformed( ActionEvent e ) - { - String command = e.getActionCommand(); - - if( command.contentEquals( "Save" ) ) - { - boolean expanded = mSystemNode.getModel().getTree() - .isExpanded( new TreePath( mSystemNode ) ); - - String name = mTextSystem.getText(); - - if( name != null ) - { - mSystemNode.getSystem().setName( name ); - mSystemNode.save(); - mSystemNode.show(); - - if( expanded ) - { - mSystemNode.getModel().getTree().expandPath( new TreePath( mSystemNode ) ); - } - } - else - { - JOptionPane.showMessageDialog( SystemEditor.this, "Please enter a system name" ); - } - } - else if( command.contentEquals( "Reset" ) ) - { - mTextSystem.setText( mSystemNode.getSystem().getName() ); - } - } -} diff --git a/src/controller/system/SystemListNode.java b/src/controller/system/SystemListNode.java deleted file mode 100644 index d0e5b3a9..00000000 --- a/src/controller/system/SystemListNode.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.system; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import playlist.PlaylistManager; -import source.SourceManager; -import controller.ConfigurableNode; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; - -public class SystemListNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SourceManager mSourceManager; - - public SystemListNode( SystemList list, - ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SourceManager sourceManager ) - { - super( playlistManager, list ); - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSourceManager = sourceManager; - } - - public SystemList getSystemList() - { - return (SystemList)getUserObject(); - } - - public void init() - { - for( System system: getSystemList().getSystem() ) - { - SystemNode node = new SystemNode( system, mChannelModel, - mChannelProcessingManager, getPlaylistManager(), mSourceManager ); - - getModel().addNode( node, SystemListNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - public String toString() - { - return "Systems [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSystemItem = new JMenuItem( "Add System" ); - addSystemItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - System system = new System(); - - getSystemList().addSystem( system ); - - getModel().addNode( new SystemNode( system, mChannelModel, - mChannelProcessingManager, getPlaylistManager(), mSourceManager ), - SystemListNode.this, SystemListNode.this.getChildCount() ); - } - } ); - - retVal.add( addSystemItem ); - - return retVal; - } -} diff --git a/src/controller/system/SystemNode.java b/src/controller/system/SystemNode.java deleted file mode 100644 index 39f28214..00000000 --- a/src/controller/system/SystemNode.java +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.system; - - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.Enumeration; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JSeparator; -import javax.swing.tree.DefaultMutableTreeNode; - -import playlist.PlaylistManager; -import source.SourceManager; -import controller.ConfigurableNode; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; -import controller.site.Site; -import controller.site.SiteNode; - -public class SystemNode extends ConfigurableNode -{ - private static final long serialVersionUID = 1L; - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SourceManager mSourceManager; - - public SystemNode( System system, - ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SourceManager sourceManager ) - { - super( playlistManager, system ); - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSourceManager = sourceManager; - } - - public void init() - { - for( Site site: getSystem().getSite() ) - { - SiteNode node = new SiteNode( site, mChannelModel, - mChannelProcessingManager, getPlaylistManager(), mSourceManager ); - - getModel().insertNodeInto( node, SystemNode.this, getChildCount() ); - - node.init(); - } - - sort(); - } - - @Override - public String getIconPath() - { - return "images/system.png"; - } - - @Override - public Color getBackgroundColor() - { - @SuppressWarnings( "unchecked" ) - Enumeration nodes = children(); - - while( nodes.hasMoreElements() ) - { - SiteNode child = (SiteNode)nodes.nextElement(); - - if( child.getBackgroundColor() != null ) - { - return Color.CYAN; - } - } - - return null; - } - - @Override - public JPanel getEditor() - { - return new SystemEditor( this ); - } - - public System getSystem() - { - return (System)getUserObject(); - } - - public String toString() - { - return getSystem().getName(); - } - - public void delete() - { - for( int x = 0; x < getChildCount(); x++ ) - { - ((SiteNode)getChildAt( x )).delete(); - } - - SystemListNode parent = (SystemListNode)getParent(); - - parent.getSystemList().removeSystem( getSystem() ); - - save(); - - getModel().removeNodeFromParent( SystemNode.this ); - - /* Show the first child, if one exists so that we don't have a - * collapsed list */ - if( parent.getChildCount() > 0 ) - { - ((ConfigurableNode)parent.getFirstChild()).show(); - } - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem addSiteItem = new JMenuItem( "Add Site" ); - addSiteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - Site site = new Site(); - getSystem().addSite( site ); - - SiteNode node = new SiteNode( site, mChannelModel, - mChannelProcessingManager, getPlaylistManager(), mSourceManager ); - - getModel().addNode( node, - SystemNode.this, - SystemNode.this.getChildCount() ); - - sort(); - - node.show(); - } - } ); - - retVal.add( addSiteItem ); - - retVal.add( new JSeparator() ); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - delete(); - } - } ); - - retVal.add( deleteItem ); - - return retVal; - } -} diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 577c6593..505f0dd9 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -74,6 +74,7 @@ import controller.channel.ChannelEventListener; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; import controller.channel.ChannelSelectionManager; +import controller.channel.map.ChannelMapModel; public class SDRTrunk { @@ -117,26 +118,27 @@ public class SDRTrunk ThreadPoolManager threadPoolManager = new ThreadPoolManager(); - SourceManager sourceManager = new SourceManager( mSettingsManager, threadPoolManager ); - - ChannelModel channelModel = new ChannelModel(); - AliasModel aliasModel = new AliasModel(); - PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, - aliasModel, channelModel ); - - RecorderManager recorderManager = new RecorderManager( threadPoolManager ); + ChannelModel channelModel = new ChannelModel(); + + ChannelMapModel channelMapModel = new ChannelMapModel(); EventLogManager eventLogManager = new EventLogManager(); + + RecorderManager recorderManager = new RecorderManager( threadPoolManager ); + SourceManager sourceManager = new SourceManager( mSettingsManager, + threadPoolManager ); + ChannelProcessingManager channelProcessingManager = - new ChannelProcessingManager( channelModel, eventLogManager, - playlistManager, recorderManager, sourceManager, threadPoolManager ); - channelModel.addListener( channelProcessingManager ); - + new ChannelProcessingManager( channelModel, channelMapModel, + aliasModel, eventLogManager, recorderManager, + sourceManager, threadPoolManager ); channelProcessingManager.addAudioPacketListener( recorderManager ); + channelModel.addListener( channelProcessingManager ); + ChannelSelectionManager channelSelectionManager = new ChannelSelectionManager( channelModel ); channelModel.addListener( channelSelectionManager ); @@ -152,27 +154,22 @@ public class SDRTrunk ConfigurationControllerModel configurationControllerModel = new ConfigurationControllerModel( channelModel, channelProcessingManager, - playlistManager, mSettingsManager, sourceManager ); + mSettingsManager, sourceManager ); mControllerPanel = new ControllerPanel( audioManager, configurationControllerModel, - aliasModel, channelModel, channelProcessingManager, mapService, playlistManager, - mSettingsManager, sourceManager ); + aliasModel, channelModel, channelMapModel, channelProcessingManager, + mapService, mSettingsManager, sourceManager ); mSpectralPanel = new SpectralDisplayPanel( configurationControllerModel, - channelModel, channelProcessingManager, playlistManager, mSettingsManager ); + channelModel, channelProcessingManager, mSettingsManager ); mTitle = getTitle(); - //TODO: move this to the end once we add the alias model, otherwise the - //configuration tree won't load correctly - //Initialize the playlist manager to load the saved playlist + PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, + aliasModel, channelModel, channelMapModel ); + playlistManager.init(); - mLog.info( "starting main application gui" ); - - //Initialize the GUI - initGUI(); - if( mLogChannelAndMemoryUsage ) { Runnable cml = new ChannelMemoryLogger(); @@ -181,6 +178,11 @@ public class SDRTrunk ThreadType.DECODER, cml, 5, TimeUnit.SECONDS ); } + mLog.info( "starting main application gui" ); + + //Initialize the GUI + initGUI(); + //Start the gui EventQueue.invokeLater( new Runnable() { diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index d96020b1..94f1eb54 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -16,6 +16,7 @@ import source.IControllableFileSource; import alias.AliasModel; import controller.ThreadPoolManager; import controller.channel.ChannelModel; +import controller.channel.map.ChannelMapModel; public class DecoderSelectionFrame extends JInternalFrame { @@ -32,10 +33,11 @@ public class DecoderSelectionFrame extends JInternalFrame { AliasModel aliasModel = new AliasModel(); ChannelModel channelModel = new ChannelModel(); + ChannelMapModel channelMapModel = new ChannelMapModel(); ThreadPoolManager tpm = new ThreadPoolManager(); - mPlaylistManager = new PlaylistManager( tpm, aliasModel, channelModel ); + mPlaylistManager = new PlaylistManager( tpm, aliasModel, channelModel, channelMapModel ); mDesktop = desktop; mSource = source; diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index a2afb439..09ea4383 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -58,6 +58,7 @@ import message.Message; import module.Module; import module.decode.DecoderFactory; import module.decode.config.DecodeConfiguration; +import module.log.EventLogManager; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; @@ -67,10 +68,12 @@ import playlist.PlaylistManager; import sample.Listener; import source.IControllableFileSource; import source.Source; +import alias.AliasModel; import controller.ThreadPoolManager; import controller.channel.Channel; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; +import controller.channel.map.ChannelMapModel; public class DecoderViewFrame extends JInternalFrame implements Listener @@ -97,12 +100,14 @@ public class DecoderViewFrame extends JInternalFrame mProcessingChain = new InstrumentableProcessingChain(); ChannelModel channelModel = new ChannelModel(); + ChannelMapModel channelMapModel = new ChannelMapModel(); + ChannelProcessingManager channelProcessingManager = - new ChannelProcessingManager( channelModel, null, - playlistManager, null, null, new ThreadPoolManager() ); + new ChannelProcessingManager( channelModel, channelMapModel, + new AliasModel(), new EventLogManager(), null, null, new ThreadPoolManager() ); List modules = DecoderFactory.getModules( channelModel, - channelProcessingManager, playlistManager, channel ); + channelMapModel, channelProcessingManager, new AliasModel(), channel ); mProcessingChain.addModules( modules ); diff --git a/src/module/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java index bd631d3b..5d460db1 100644 --- a/src/module/decode/DecodeComponentEditor.java +++ b/src/module/decode/DecodeComponentEditor.java @@ -31,10 +31,10 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import controller.channel.Channel; import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; +import controller.channel.map.ChannelMapModel; public class DecodeComponentEditor extends ChannelConfigurationEditor { @@ -42,17 +42,14 @@ public class DecodeComponentEditor extends ChannelConfigurationEditor private static final long serialVersionUID = 1L; - private PlaylistManager mPlaylistManager; private JComboBox mComboDecoders; private DecodeEditor mEditor; private Channel mChannel; - public DecodeComponentEditor( PlaylistManager playlistManager ) + public DecodeComponentEditor( final ChannelMapModel channelMapModel ) { - mPlaylistManager = playlistManager; - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); mComboDecoders = new JComboBox(); @@ -96,8 +93,7 @@ public class DecodeComponentEditor extends ChannelConfigurationEditor } //Change to the new one - mEditor = DecoderFactory.getEditorPanel( config, - mChannel, mPlaylistManager ); + mEditor = DecoderFactory.getEditorPanel( config, channelMapModel ); add( mEditor, "span 2,center" ); diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index 447e7865..c9304b62 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -97,12 +97,14 @@ import record.config.RecordConfiguration; import settings.SettingsManager; import util.TimeStamp; import alias.AliasList; +import alias.AliasModel; import audio.AudioModule; import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; import controller.channel.map.ChannelMap; +import controller.channel.map.ChannelMapModel; import filter.AllPassFilter; import filter.FilterSet; import filter.IFilter; @@ -123,17 +125,17 @@ public class DecoderFactory * @return list of configured decoders */ public static List getModules( ChannelModel channelModel, + ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, + AliasModel aliasModel, Channel channel ) { /* Get the optional alias list for the decode modules to use */ - AliasList aliasList = playlistManager.getPlayist() - .getAliasDirectory().getAliasList( channel.getAliasListName() ); + AliasList aliasList = aliasModel.getAliasList( channel.getAliasListName() ); - List modules = getPrimaryModules( channelModel, channelProcessingManager, - playlistManager, aliasList, channel ); + List modules = getPrimaryModules( channelModel, channelMapModel, + channelProcessingManager, aliasList, channel ); modules.addAll( getAuxiliaryDecoders( channel.getAuxDecodeConfiguration(), aliasList ) ); @@ -144,8 +146,8 @@ public class DecoderFactory * Constructs a primary decoder as specified in the decode configuration */ public static List getPrimaryModules( ChannelModel channelModel, + ChannelMapModel channelMapModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, AliasList aliasList, Channel channel ) { @@ -198,8 +200,8 @@ public class DecoderFactory case MPT1327: DecodeConfigMPT1327 mptConfig = (DecodeConfigMPT1327)decodeConfig; - ChannelMap channelMap = playlistManager.getPlayist() - .getChannelMapList().getChannelMap( mptConfig.getChannelMapName() ); + ChannelMap channelMap = channelMapModel + .getChannelMap( mptConfig.getChannelMapName() ); Sync sync = mptConfig.getSync(); @@ -432,7 +434,7 @@ public class DecoderFactory } public static DecodeEditor getEditorPanel( DecodeConfiguration config, - Channel channel, PlaylistManager playlistManager ) + ChannelMapModel channelMapModel ) { DecodeEditor configuredPanel; @@ -449,7 +451,7 @@ public class DecoderFactory break; case MPT1327: configuredPanel = new MPT1327ConfigEditor( config, - channel, playlistManager ); + channelMapModel ); break; case PASSPORT: configuredPanel = new PassportEditor( config ); diff --git a/src/module/decode/mpt1327/MPT1327ConfigEditor.java b/src/module/decode/mpt1327/MPT1327ConfigEditor.java index fa036c96..586a605d 100644 --- a/src/module/decode/mpt1327/MPT1327ConfigEditor.java +++ b/src/module/decode/mpt1327/MPT1327ConfigEditor.java @@ -17,10 +17,13 @@ ******************************************************************************/ package module.decode.mpt1327; -import java.util.ArrayList; +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.List; import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JSlider; @@ -30,9 +33,9 @@ import javax.swing.event.ChangeListener; import module.decode.DecodeEditor; import module.decode.config.DecodeConfiguration; import module.decode.mpt1327.MPT1327Decoder.Sync; -import playlist.PlaylistManager; -import controller.channel.Channel; import controller.channel.map.ChannelMap; +import controller.channel.map.ChannelMapManagerFrame; +import controller.channel.map.ChannelMapModel; public class MPT1327ConfigEditor extends DecodeEditor { @@ -44,17 +47,14 @@ public class MPT1327ConfigEditor extends DecodeEditor private JSlider mCallTimeout; private JLabel mTrafficChannelPoolSizeLabel; private JSlider mTrafficChannelPoolSize; - private Channel mChannel; - private PlaylistManager mPlaylistManager; + private ChannelMapModel mChannelMapModel; public MPT1327ConfigEditor( DecodeConfiguration config, - Channel channel, - PlaylistManager playlistManager ) + ChannelMapModel channelMapModel ) { super( config ); - mChannel = channel; - mPlaylistManager = playlistManager; + mChannelMapModel = channelMapModel; initGUI(); } @@ -69,10 +69,7 @@ public class MPT1327ConfigEditor extends DecodeEditor /** * ComboBox: Alias Lists */ - List maps = ( mPlaylistManager == null ? - new ArrayList() : mPlaylistManager.getPlayist() - .getChannelMapList().getChannelMap() ); - + List maps = mChannelMapModel.getChannelMaps(); ChannelMap[] mapArray = maps.toArray( new ChannelMap[ maps.size() ] ); mComboChannelMaps = new JComboBox(); @@ -82,6 +79,27 @@ public class MPT1327ConfigEditor extends DecodeEditor add( new JLabel( "Channel Map:" ) ); add( mComboChannelMaps, "wrap" ); + JButton channelMapManager = new JButton( "Channel Map Manager ..." ); + channelMapManager.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final ChannelMapManagerFrame manager = + new ChannelMapManagerFrame( mChannelMapModel ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + manager.setVisible( true ); + } + } ); + } + } ); + add( channelMapManager, "wrap" ); + mComboSync = new JComboBox(); mComboSync.setModel( new DefaultComboBoxModel( Sync.values() ) ); diff --git a/src/module/decode/p25/P25Decoder.java b/src/module/decode/p25/P25Decoder.java index 5c879e5e..184c6f88 100644 --- a/src/module/decode/p25/P25Decoder.java +++ b/src/module/decode/p25/P25Decoder.java @@ -1,9 +1,9 @@ package module.decode.p25; +import alias.AliasList; import instrument.Instrumentable; import module.decode.Decoder; import module.decode.DecoderType; -import alias.AliasList; public abstract class P25Decoder extends Decoder implements Instrumentable { diff --git a/src/module/decode/p25/P25MessageProcessor.java b/src/module/decode/p25/P25MessageProcessor.java index 0ef49de5..d34437bd 100644 --- a/src/module/decode/p25/P25MessageProcessor.java +++ b/src/module/decode/p25/P25MessageProcessor.java @@ -10,8 +10,8 @@ import module.decode.p25.message.ldu.LDUMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import sample.Listener; import alias.AliasList; +import sample.Listener; public class P25MessageProcessor implements Listener { diff --git a/src/module/decode/p25/P25_C4FMDecoder.java b/src/module/decode/p25/P25_C4FMDecoder.java index c9f8ba7f..650c0cc2 100644 --- a/src/module/decode/p25/P25_C4FMDecoder.java +++ b/src/module/decode/p25/P25_C4FMDecoder.java @@ -30,13 +30,13 @@ import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import alias.AliasList; import sample.Listener; import sample.real.IFilteredRealBufferListener; import sample.real.RealBuffer; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.IFrequencyChangeListener; import source.tuner.frequency.IFrequencyChangeProvider; -import alias.AliasList; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.fir.real.RealFIRFilter_RB_RB; diff --git a/src/module/decode/p25/P25_LSMDecoder.java b/src/module/decode/p25/P25_LSMDecoder.java index b6e64bf3..011ff83d 100644 --- a/src/module/decode/p25/P25_LSMDecoder.java +++ b/src/module/decode/p25/P25_LSMDecoder.java @@ -31,11 +31,11 @@ import java.util.concurrent.ScheduledExecutorService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import alias.AliasList; import sample.Listener; import sample.complex.ComplexBuffer; import sample.complex.ComplexBufferToStreamConverter; import sample.complex.IComplexBufferListener; -import alias.AliasList; import dsp.filter.FilterFactory; import dsp.filter.Window.WindowType; import dsp.filter.fir.complex.ComplexFIRFilter_CB_CB; diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index fc333914..3c781462 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -34,7 +34,6 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.ColorSettingResetMenuItem; @@ -60,17 +59,14 @@ public class ChannelList extends JPanel implements ChannelEventListener private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; - private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; public ChannelList( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, SettingsManager settingsManager ) { mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; mSettingsManager = settingsManager; init(); @@ -105,7 +101,7 @@ public class ChannelList extends JPanel implements ChannelEventListener if( !mDisplayedPanels.containsKey( channel ) ) { ChannelPanel channelPanel = new ChannelPanel( mChannelModel, - mChannelProcessingManager, mPlaylistManager, mSettingsManager, channel ); + mChannelProcessingManager, mSettingsManager, channel ); add( channelPanel, "wrap" ); diff --git a/src/module/decode/state/ChannelPanel.java b/src/module/decode/state/ChannelPanel.java index 5dee9814..dd51716b 100644 --- a/src/module/decode/state/ChannelPanel.java +++ b/src/module/decode/state/ChannelPanel.java @@ -76,7 +76,6 @@ public class ChannelPanel extends JPanel private JLabel mSiteLabel; private JLabel mChannelLabel; - private PlaylistManager mPlaylistManager; protected SettingsManager mSettingsManager; private Channel mChannel; @@ -93,13 +92,11 @@ public class ChannelPanel extends JPanel */ public ChannelPanel( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, SettingsManager settingsManager, Channel channel ) { mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; mSettingsManager = settingsManager; mChannel = channel; @@ -198,8 +195,6 @@ public class ChannelPanel extends JPanel mChannelProcessingManager = null; - mPlaylistManager = null; - mSettingsManager.removeListener( this ); mSettingsManager = null; @@ -434,7 +429,7 @@ public class ChannelPanel extends JPanel public JMenu getContextMenu() { return ChannelUtils.getContextMenu( mChannelModel, mChannelProcessingManager, - mPlaylistManager, mChannel, ChannelPanel.this ); + mChannel, ChannelPanel.this ); } @Override diff --git a/src/playlist/PlaylistManager.java b/src/playlist/PlaylistManager.java index 86e447a7..df075a70 100644 --- a/src/playlist/PlaylistManager.java +++ b/src/playlist/PlaylistManager.java @@ -36,34 +36,29 @@ import javax.xml.bind.Unmarshaller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.version1.PlaylistConverterV1ToV2; import properties.SystemProperties; import sample.Listener; -import alias.Alias; -import alias.AliasDirectory; import alias.AliasEvent; -import alias.AliasList; import alias.AliasModel; -import alias.Group; import controller.ThreadPoolManager; -import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; import controller.channel.ChannelModel; -import controller.site.Site; -import controller.system.SystemList; +import controller.channel.map.ChannelMapEvent; +import controller.channel.map.ChannelMapModel; -public class PlaylistManager implements ChannelEventListener, Listener +public class PlaylistManager implements ChannelEventListener { private final static Logger mLog = LoggerFactory.getLogger( PlaylistManager.class ); - private Playlist mPlaylist = new Playlist(); - private ThreadPoolManager mThreadPoolManager; private AliasModel mAliasModel; private ChannelModel mChannelModel; - + private ChannelMapModel mChannelMapModel; + private Path mCurrentPlaylistPath; private AtomicBoolean mPlaylistSavePending = new AtomicBoolean(); private boolean mPlaylistLoading = false; @@ -79,37 +74,35 @@ public class PlaylistManager implements ChannelEventListener, Listener() + { + @Override + public void receive( AliasEvent t ) + { + schedulePlaylistSave(); + } + } ); + + mChannelMapModel.addListener( new Listener() + { + @Override + public void receive( ChannelMapEvent t ) + { + schedulePlaylistSave(); + } + } ); } /** @@ -123,41 +116,64 @@ public class PlaylistManager implements ChannelEventListener, Listener 10 ) - { - mLog.error( "Couldn't create playlist backup - maximum revisions exceeded" ); - return; - } - } - - File outputFile = new File( filePath.toString() ); - - try - { - if( !outputFile.exists() ) - { - outputFile.createNewFile(); - } - } - catch( Exception e ) - { - mLog.error( "PlaylistManager - couldn't create file to save " - + "playlist backup [" + filePath.toString() + "]" ); - } - - try( OutputStream out = new FileOutputStream( outputFile ) ) - { - Files.copy( current, out ); - - mLog.info( "Playlist backed up to:" + filePath ); - } - catch( IOException ioe ) - { - mLog.error( "Error copying playlist to backup file [" + filePath + "]" ); - } - } - else - { - mLog.error( "Couldn't create backup playlist - original playlist does not exist" ); - } - } - /** - * Erases current playlist and loads playlist from the playlistPath filename, - * if it exists. + * Loads a version 2 playlist */ - public void load( Path playlistPath ) + public PlaylistV2 load( Path playlistPath ) { + mLog.info( "loading version 2 playlist file [" + playlistPath.toString() + "]" ); + + PlaylistV2 playlist = null; + if( Files.exists( playlistPath ) ) { - mLog.info( "loading playlist file [" + playlistPath.toString() + "]" ); - JAXBContext context = null; InputStream in = null; @@ -365,11 +317,11 @@ public class PlaylistManager implements ChannelEventListener, Listener - ******************************************************************************/ -package playlist; - -import javax.swing.JPanel; - -import settings.SettingsManager; -import source.SourceManager; -import alias.AliasDirectory; -import alias.AliasDirectoryNode; -import controller.BaseNode; -import controller.EmptyEditor; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; -import controller.channel.map.ChannelMapList; -import controller.channel.map.ChannelMapListNode; -import controller.system.SystemList; -import controller.system.SystemListNode; - -public class PlaylistNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - private AliasDirectoryNode mAliasDirectoryNode; - private ChannelMapListNode mChannelMapListNode; - private SystemListNode mSystemListNode; - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private PlaylistManager mPlaylistManager; - private SettingsManager mSettingsManager; - private SourceManager mSourceManager; - - public PlaylistNode( ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, - SettingsManager settingsManager, - SourceManager sourceManager ) - { - super( null ); - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; - mSettingsManager = settingsManager; - mSourceManager = sourceManager; - } - - private void init() - { - /** - * Delete any existing children - */ - delete(); - - /** - * Load the AliasDirectory - */ - AliasDirectory directory = getPlaylist().getAliasDirectory(); - - if( directory == null ) - { - directory = new AliasDirectory(); - getPlaylist().setAliasDirectory( directory ); - } - - mAliasDirectoryNode = new AliasDirectoryNode( mPlaylistManager, - mSettingsManager, directory ); - - getModel().insertNodeInto( mAliasDirectoryNode, this, 0 ); - - mAliasDirectoryNode.init(); - - /** - * Load the Channel Map List - */ - ChannelMapList channelMapList = getPlaylist().getChannelMapList(); - - if( channelMapList == null ) - { - channelMapList = new ChannelMapList(); - } - - mChannelMapListNode = new ChannelMapListNode( mPlaylistManager, channelMapList ); - - getModel().insertNodeInto( mChannelMapListNode, this, 1 ); - - mChannelMapListNode.init(); - - /** - * Load the SystemList - */ - SystemList list = getPlaylist().getSystemList(); - - if( list == null ) - { - list = new SystemList(); - getPlaylist().setSystemList( list ); - } - - mSystemListNode = new SystemListNode( list, mChannelModel, - mChannelProcessingManager, mPlaylistManager, mSourceManager ); - - getModel().insertNodeInto( mSystemListNode, this, 2 ); - - mSystemListNode.init(); - - mSystemListNode.show(); - } - - public SystemListNode getSystemListNode() - { - return mSystemListNode; - } - - public AliasDirectoryNode getAliasDirectoryNode() - { - return mAliasDirectoryNode; - } - - public void loadPlaylist() - { - Playlist playlist = mPlaylistManager.getPlayist(); - - setUserObject( playlist ); - - init(); - } - - - public Playlist getPlaylist() - { - Playlist retVal = null; - - Object userObject = getUserObject(); - - if( userObject != null ) - { - retVal = (Playlist)userObject; - } - - return retVal; - } - - public String toString() - { - return "Playlist"; - } - - public JPanel getEditor() - { - return new EmptyEditor(); - } -} diff --git a/src/playlist/PlaylistV2.java b/src/playlist/PlaylistV2.java new file mode 100644 index 00000000..aa3fa2fe --- /dev/null +++ b/src/playlist/PlaylistV2.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package playlist; + +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import alias.Alias; +import controller.channel.Channel; +import controller.channel.map.ChannelMap; + +@XmlSeeAlso( { Alias.class,Channel.class,ChannelMap.class } ) + +@XmlRootElement( name = "playlist" ) +public class PlaylistV2 +{ + //Version 2 member variables + private List mAliases = new ArrayList<>(); + private List mChannels = new ArrayList<>(); + private List mChannelMaps = new ArrayList<>(); + + public PlaylistV2() + { + } + + @XmlElement( name = "alias" ) + public List getAliases() + { + return mAliases; + } + + public void setAliases( List aliases ) + { + mAliases = aliases; + } + + @XmlElement( name = "channel" ) + public List getChannels() + { + return mChannels; + } + + public void setChannels( List channels ) + { + mChannels = channels; + } + + @XmlElement( name = "channel_map" ) + public List getChannelMaps() + { + return mChannelMaps; + } + + public void setChannelMaps( List channelMaps ) + { + mChannelMaps = channelMaps; + } +} diff --git a/src/alias/AliasDirectory.java b/src/playlist/version1/AliasDirectory.java similarity index 76% rename from src/alias/AliasDirectory.java rename to src/playlist/version1/AliasDirectory.java index 34202b2f..49c64eb7 100644 --- a/src/alias/AliasDirectory.java +++ b/src/playlist/version1/AliasDirectory.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package playlist.version1; import java.util.ArrayList; @@ -23,11 +23,14 @@ import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; +import alias.AliasList; + @XmlSeeAlso( { AliasList.class} ) @XmlRootElement( name = "alias_directory" ) +@Deprecated public class AliasDirectory { - private ArrayList mAliasList = new ArrayList(); + private ArrayList mAliasList = new ArrayList<>(); public AliasDirectory() { @@ -35,10 +38,6 @@ public class AliasDirectory public void refresh() { - for( AliasList list: mAliasList ) - { - list.update(); - } } public void clearAliasLists() @@ -47,27 +46,27 @@ public class AliasDirectory } @XmlElement( name = "alias_list" ) - public ArrayList getAliasList() + public ArrayList getAliasList() { return mAliasList; } - public void setAliasList( ArrayList lists ) + public void setAliasList( ArrayList lists ) { mAliasList = lists; if( mAliasList == null ) { - mAliasList = new ArrayList(); + mAliasList = new ArrayList(); } } - public void addAliasList( AliasList list ) + public void addAliasList( AliasListOld list ) { mAliasList.add( list ); } - public void removeAliasList( AliasList list ) + public void removeAliasList( AliasListOld list ) { mAliasList.remove( list ); } @@ -75,11 +74,11 @@ public class AliasDirectory /** * Gets the named alias list, or returns an empty alias list */ - public AliasList getAliasList( String name ) + public AliasListOld getAliasList( String name ) { - AliasList retVal = new AliasList(); + AliasListOld retVal = new AliasListOld( name ); - for( AliasList list: mAliasList ) + for( AliasListOld list: mAliasList ) { if( list.getName().equalsIgnoreCase( name ) ) { diff --git a/src/playlist/version1/AliasListOld.java b/src/playlist/version1/AliasListOld.java new file mode 100644 index 00000000..7df3c6be --- /dev/null +++ b/src/playlist/version1/AliasListOld.java @@ -0,0 +1,583 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014,2015 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 + ******************************************************************************/ +package playlist.version1; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.bind.annotation.XmlAttribute; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlSeeAlso; + +import module.decode.lj1200.LJ1200Message.Function; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import alias.Alias; +import alias.action.AliasAction; +import alias.action.beep.BeepAction; +import alias.action.clip.ClipAction; +import alias.action.script.ScriptAction; +import alias.id.AliasID; +import alias.id.esn.Esn; +import alias.id.fleetsync.FleetsyncID; +import alias.id.lojack.LoJackFunctionAndID; +import alias.id.mdc.MDC1200ID; +import alias.id.mobileID.Min; +import alias.id.mpt1327.MPT1327ID; +import alias.id.nonrecordable.NonRecordable; +import alias.id.priority.Priority; +import alias.id.siteID.SiteID; +import alias.id.status.StatusID; +import alias.id.talkgroup.TalkgroupID; +import alias.id.uniqueID.UniqueID; +import audio.metadata.MetadataType; + +@XmlSeeAlso( { Alias.class, + + /* Alias ID's */ + AliasID.class, + FleetsyncID.class, + Esn.class, + Group.class, + LoJackFunctionAndID.class, + MDC1200ID.class, + Min.class, + MPT1327ID.class, + UniqueID.class, + SiteID.class, + StatusID.class, + TalkgroupID.class, + + /* Alias Attributes */ + NonRecordable.class, + Priority.class, + + /* Alias Actions */ + AliasAction.class, + BeepAction.class, + ClipAction.class, + ScriptAction.class } ) + +@Deprecated +@XmlRootElement( name = "alias_list" ) +public class AliasListOld implements Comparable +{ + private final static Logger mLog = LoggerFactory.getLogger( AliasListOld.class ); + + private String mName; + private ArrayList mGroups = new ArrayList(); + + private Map mESN = new HashMap<>(); + private Map mFleetsync = new HashMap<>(); + private Map mLoJack = new HashMap<>(); + private Map mMDC1200 = new HashMap<>(); + private Map mMobileID = new HashMap<>(); + private Map mMPT1327 = new HashMap<>(); + private Map mSiteID = new HashMap<>(); + private Map mStatus = new HashMap<>(); + private Map mTalkgroup = new HashMap<>(); + private Map mUniqueID = new HashMap<>(); + + private boolean mESNWildcard = false; + private boolean mMobileIDWildcard = false; + private boolean mFleetsyncWildcard = false; + private boolean mMDC1200Wildcard = false; + private boolean mMPT1327Wildcard = false; + private boolean mSiteWildcard = false; + private boolean mTalkgroupWildcard = false; + + public AliasListOld() + { + this( null ); + } + + public AliasListOld( String name ) + { + mName = name; + update(); + } + + /** + * Load/Reload all lookup hashmaps + */ + public void update() + { + //Clear hashmaps + mESN.clear(); + mFleetsync.clear(); + mLoJack.clear(); + mMDC1200.clear(); + mMobileID.clear(); + mMPT1327.clear(); + mSiteID.clear(); + mStatus.clear(); + mTalkgroup.clear(); + mUniqueID.clear(); + + for( Group group: mGroups ) + { + for( Alias alias: group.getAlias() ) + { + for( AliasID id: alias.getId() ) + { + switch( id.getType() ) + { + case ESN: + Esn esn = (Esn)id; + if( esn.getEsn().contains( "*" ) ) + { + mESNWildcard = true; + + mESN.put( fixWildcard( esn.getEsn() ), alias ); + } + else + { + mESN.put( esn.getEsn(), alias ); + } + break; + case Fleetsync: + FleetsyncID fs = (FleetsyncID)id; + + if( fs.getIdent().contains( "*" ) ) + { + mFleetsyncWildcard = true; + + mFleetsync.put( fixWildcard( fs.getIdent() ), alias ); + } + else + { + mFleetsync.put( fs.getIdent(), alias ); + } + break; + case LoJack: + mLoJack.put( (LoJackFunctionAndID)id, alias ); + break; + case MDC1200: + MDC1200ID mdc = (MDC1200ID)id; + + if( mdc.getIdent().contains( "*" ) ) + { + mMDC1200Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMDC1200.put( fixWildcard( mdc.getIdent() ), alias ); + } + else + { + mMDC1200.put( mdc.getIdent(), alias ); + } + break; + case MPT1327: + MPT1327ID mpt = (MPT1327ID)id; + + String ident = mpt.getIdent(); + + if( ident != null ) + { + if( ident.contains( "*" ) ) + { + mMPT1327Wildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mMPT1327.put( fixWildcard( ident ), alias ); + } + else + { + mMPT1327.put( ident, alias ); + } + } + break; + case MIN: + Min min = (Min)id; + + if( min.getMin().contains( "*" ) ) + { + mMobileIDWildcard = true; + mMobileID.put( fixWildcard( min.getMin() ), alias ); + } + else + { + mMobileID.put( min.getMin(), alias ); + } + break; + case LTRNetUID: + UniqueID uid = (UniqueID)id; + + mUniqueID.put( uid.getUid(), alias ); + break; + case Site: + SiteID siteID = (SiteID)id; + + if( siteID.getSite().contains( "*" ) ) + { + mSiteWildcard = true; + mSiteID.put( fixWildcard( siteID.getSite() ), alias ); + } + else + { + mSiteID.put( siteID.getSite(), alias ); + } + + break; + case Status: + mStatus.put( ((StatusID)id).getStatus(), alias ); + break; + case Talkgroup: + TalkgroupID tgid = (TalkgroupID)id; + + if( tgid.getTalkgroup().contains( "*" ) ) + { + mTalkgroupWildcard = true; + + //Replace (*) wildcard with regex wildcard (.) + mTalkgroup.put( fixWildcard( tgid.getTalkgroup() ), alias ); + } + else + { + mTalkgroup.put( tgid.getTalkgroup(), alias ); + } + break; + case NonRecordable: + case Priority: + //We don't maintain lookups for these items + break; + default: + mLog.warn( "Unrecognized Alias ID Type:" + id.getType().name() ); + break; + } + } + } + } + } + + /** + * Converts user wildcard character (*) to regex single character wildcard (.) + * but ignores a regex multi-character wildcard (.*) + */ + private String fixWildcard( String value ) + { + if( value.contains( "*" ) && !value.contains( ".*" ) ) + { + return value.replace( "*", "." ); + } + + return value; + } + + public Alias getSiteID( String siteID ) + { + if( siteID != null ) + { + if( mSiteWildcard ) + { + for( String regex: mSiteID.keySet() ) + { + if( siteID.matches( regex ) ) + { + return mSiteID.get( regex ); + } + } + } + else + { + return mSiteID.get( siteID ); + } + } + + return null; + } + + public Alias getStatus( int status ) + { + return mStatus.get( status ); + } + + public Alias getUniqueID( int uniqueID ) + { + return mUniqueID.get( uniqueID ); + } + + public Alias getESNAlias( String esn ) + { + if( esn != null ) + { + if( mESNWildcard ) + { + for( String regex: mESN.keySet() ) + { + if( esn.matches( regex ) ) + { + return mESN.get( regex ); + } + } + } + else + { + return mESN.get( esn ); + } + } + + return null; + } + + + public Alias getFleetsyncAlias( String ident ) + { + if( ident != null ) + { + if( mFleetsyncWildcard ) + { + for( String regex: mFleetsync.keySet() ) + { + if( ident.matches( regex ) ) + { + return mFleetsync.get( regex ); + } + } + } + else + { + return mFleetsync.get( ident ); + } + } + + return null; + } + + public Alias getLoJackAlias( Function function, String id ) + { + if( id != null ) + { + for( LoJackFunctionAndID lojack: mLoJack.keySet() ) + { + if( lojack.matches( function, id ) ) + { + return mLoJack.get( lojack ); + } + } + } + + return null; + } + + public Alias getMDC1200Alias( String ident ) + { + if( ident != null ) + { + if( mMDC1200Wildcard ) + { + for( String regex: mMDC1200.keySet() ) + { + if( ident.matches( regex ) ) + { + return mMDC1200.get( regex ); + } + } + } + else + { + return mMDC1200.get( ident ); + } + } + + return null; + } + + public Alias getMPT1327Alias( String ident ) + { + if( ident != null ) + { + if( mMPT1327Wildcard ) + { + for( String regex: mMPT1327.keySet() ) + { + if( ident.matches( regex ) ) + { + return mMPT1327.get( regex ); + } + } + } + else + { + return mMPT1327.get( ident ); + } + } + + return null; + } + + public Group getGroup( Alias alias ) + { + if( alias != null ) + { + for( Group group: mGroups ) + { + if( group.contains( alias ) ) + { + return group; + } + } + } + + return null; + } + + public Alias getMobileIDNumberAlias( String ident ) + { + if( ident != null ) + { + if( mMobileIDWildcard ) + { + for( String regex: mMobileID.keySet() ) + { + if( ident.matches( regex ) ) + { + return mMobileID.get( regex ); + } + } + } + else + { + return mMobileID.get( ident ); + } + } + + return null; + } + + public Alias getTalkgroupAlias( String tgid ) + { + if( tgid != null ) + { + if( mTalkgroupWildcard ) + { + for( String regex: mTalkgroup.keySet() ) + { + if( tgid.matches( regex ) ) + { + return mTalkgroup.get( regex ); + } + } + } + else + { + return mTalkgroup.get( tgid ); + } + } + + return null; + } + + public String toString() + { + return mName; + } + + @XmlAttribute + public String getName() + { + return mName; + } + + public void setName( String name ) + { + mName = name; + } + + public ArrayList getGroup() + { + return mGroups; + } + + public void setGroup( ArrayList groups ) + { + mGroups = groups; + } + + public void addGroup( Group group ) + { + mGroups.add( group ); + } + + public void removeGroup( Group group ) + { + mGroups.remove( group ); + } + + @Override + public int compareTo( AliasListOld otherAliasList ) + { + return getName().compareTo( otherAliasList.getName() ); + } + + public Alias getAlias( String value, MetadataType type ) + { + switch( type ) + { + case ESN: + return getESNAlias( value ); + case FLEETSYNC: + return getFleetsyncAlias( value ); + case MDC1200: + return getMDC1200Alias( value ); + case MOBILE_ID: + return getMobileIDNumberAlias( value ); + case MPT1327: + return getMPT1327Alias( value ); + case SITE_ID: + return getSiteID( value ); + case STATUS: + if( value != null ) + { + try + { + return getStatus( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + case FROM: + case TO: + return getTalkgroupAlias( value ); + case UNIQUE_ID: + if( value != null ) + { + try + { + return getUniqueID( Integer.valueOf( value ) ); + } + catch( Exception e ) + { + //do nothing, we couldn't parse the int value + } + } + break; + default: + break; + } + + return null; + } + +} diff --git a/src/controller/channel/map/ChannelMapList.java b/src/playlist/version1/ChannelMapList.java similarity index 96% rename from src/controller/channel/map/ChannelMapList.java rename to src/playlist/version1/ChannelMapList.java index 7a43b25b..5d0f6d61 100644 --- a/src/controller/channel/map/ChannelMapList.java +++ b/src/playlist/version1/ChannelMapList.java @@ -15,14 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.channel.map; +package playlist.version1; import java.util.ArrayList; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSeeAlso; +import controller.channel.map.ChannelMap; + @XmlSeeAlso( ChannelMap.class ) +@Deprecated public class ChannelMapList { private ArrayList mChannelMap = new ArrayList(); diff --git a/src/alias/Group.java b/src/playlist/version1/Group.java similarity index 96% rename from src/alias/Group.java rename to src/playlist/version1/Group.java index d5e30228..d8a17d0f 100644 --- a/src/alias/Group.java +++ b/src/playlist/version1/Group.java @@ -15,12 +15,15 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package alias; +package playlist.version1; import java.util.ArrayList; import javax.xml.bind.annotation.XmlAttribute; +import alias.Alias; + +@Deprecated public class Group { private String mName; diff --git a/src/playlist/version1/PlaylistConverterV1ToV2.java b/src/playlist/version1/PlaylistConverterV1ToV2.java new file mode 100644 index 00000000..ee2f3296 --- /dev/null +++ b/src/playlist/version1/PlaylistConverterV1ToV2.java @@ -0,0 +1,168 @@ +package playlist.version1; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import playlist.PlaylistV2; +import alias.Alias; +import alias.AliasList; +import controller.channel.Channel; +import controller.channel.map.ChannelMap; + +public class PlaylistConverterV1ToV2 +{ + private final static Logger mLog = + LoggerFactory.getLogger( PlaylistConverterV1ToV2.class ); + + private PlaylistV2 mConvertedPlaylist; + private List mErrors = new ArrayList<>(); + + public PlaylistConverterV1ToV2( Path path ) + { + PlaylistV1 playlistVersion1 = null; + + if( path != null && Files.exists( path ) ) + { + mLog.info( "attempting playlist v1 to v2 conversion [" + + path.toString() + "]" ); + + JAXBContext context = null; + + InputStream in = null; + + try + { + in = new FileInputStream( path.toString() ); + + try + { + context = JAXBContext.newInstance( PlaylistV1.class ); + + Unmarshaller m = context.createUnmarshaller(); + + playlistVersion1 = (PlaylistV1)m.unmarshal( in ); + } + catch ( JAXBException e ) + { + mErrors.add( "Couldn't unmarshall version 1 playlist - " + + e.getLocalizedMessage() ); + } + } + catch ( Exception e ) + { + mErrors.add( "Couldn't open version 1 playlist - [" + + path.toString() + "] - " + e.getLocalizedMessage() ); + } + finally + { + if( in != null ) + { + try + { + in.close(); + } + catch ( IOException e ) + { + mLog.error( "exception while closing " + + "the playlist file inputstream reader", e ); + } + } + } + } + else + { + mErrors.add( "version 1 playlist is null or not found [" + + path.toString() + "]" ); + } + + if( playlistVersion1 != null ) + { + mConvertedPlaylist = new PlaylistV2(); + + if( playlistVersion1.hasSystemList() ) + { + SystemList systemList = playlistVersion1.getSystemList(); + + for( playlist.version1.System system: systemList.getSystem() ) + { + for( Site site: system.getSite() ) + { + for( Channel channel: site.getChannel() ) + { + channel.setSystem( system.getName() ); + channel.setSite( site.getName() ); + + mConvertedPlaylist.getChannels().add( channel ); + } + } + } + } + + if( playlistVersion1.hasAliasDirectory() ) + { + AliasDirectory aliasDirectory = playlistVersion1.getAliasDirectory(); + + for( AliasListOld list: aliasDirectory.getAliasList() ) + { + for( Group group: list.getGroup() ) + { + for( Alias alias: group.getAlias() ) + { + alias.setList( list.getName() ); + alias.setGroup( group.getName() ); + + mConvertedPlaylist.getAliases().add( alias ); + } + } + } + } + + if( playlistVersion1.hasChannelMapList() ) + { + //Move the channel maps out of the channel map list + ChannelMapList channelMapList = playlistVersion1.getChannelMapList(); + + List channelMaps = channelMapList.getChannelMap(); + + if( channelMaps != null && !channelMaps.isEmpty() ) + { + mConvertedPlaylist.getChannelMaps().addAll( channelMaps ); + } + } + + mLog.info( "Converted [" + mConvertedPlaylist.getChannels().size() + + "] channels to new playlist format" ); + mLog.info( "Converted [" + mConvertedPlaylist.getAliases().size() + + "] aliases to new playlist format" ); + mLog.info( "Converted [" + mConvertedPlaylist.getChannelMaps().size() + + "] channel maps to new playlist format" ); + } + } + + public PlaylistV2 getConvertedPlaylist() + { + return mConvertedPlaylist; + } + + public List getErrorMessages() + { + return mErrors; + } + + public boolean hasErrorMessages() + { + return !mErrors.isEmpty(); + } +} diff --git a/src/playlist/Playlist.java b/src/playlist/version1/PlaylistV1.java similarity index 78% rename from src/playlist/Playlist.java rename to src/playlist/version1/PlaylistV1.java index d4ae9d0c..52e8dc4a 100644 --- a/src/playlist/Playlist.java +++ b/src/playlist/version1/PlaylistV1.java @@ -15,20 +15,14 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package playlist; - -import java.util.ArrayList; -import java.util.List; +package playlist.version1; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import alias.Alias; -import alias.AliasDirectory; import controller.channel.Channel; -import controller.channel.map.ChannelMapList; -import controller.system.SystemList; @XmlSeeAlso( { AliasDirectory.class, Alias.class, @@ -36,41 +30,19 @@ import controller.system.SystemList; ChannelMapList.class, SystemList.class } ) +@Deprecated @XmlRootElement( name = "playlist" ) -public class Playlist +public class PlaylistV1 { + //Note: these are for legacy version 1 format playlists private AliasDirectory mAliasDirectory = new AliasDirectory(); private ChannelMapList mChannelMapList = new ChannelMapList(); private SystemList mSystemList = new SystemList(); - private List mAliases = new ArrayList<>(); - private List mChannels = new ArrayList<>(); - - public Playlist() + + public PlaylistV1() { } - @XmlElement( name = "alias" ) - public List getAliases() - { - return mAliases; - } - - public void setAliases( List aliases ) - { - mAliases = aliases; - } - - @XmlElement( name = "channel" ) - public List getChannels() - { - return mChannels; - } - - public void setChannels( List channels ) - { - mChannels = channels; - } - @XmlElement( name = "alias_directory" ) public AliasDirectory getAliasDirectory() { @@ -79,6 +51,7 @@ public class Playlist return mAliasDirectory; } + public void setAliasDirectory( AliasDirectory directory ) { mAliasDirectory = directory; @@ -90,6 +63,7 @@ public class Playlist mAliasDirectory.getAliasList() != null && !mAliasDirectory.getAliasList().isEmpty(); } + @XmlElement( name = "system_list" ) public SystemList getSystemList() @@ -97,6 +71,7 @@ public class Playlist return mSystemList; } + public void setSystemList( SystemList list ) { mSystemList = list; @@ -109,12 +84,21 @@ public class Playlist !mSystemList.getSystem().isEmpty(); } + @XmlElement( name = "channel_maps" ) public ChannelMapList getChannelMapList() { return mChannelMapList; } + public boolean hasChannelMapList() + { + return mChannelMapList != null && + mChannelMapList.getChannelMap() != null && + !mChannelMapList.getChannelMap().isEmpty(); + } + + public void setChannelMapList( ChannelMapList list ) { mChannelMapList = list; diff --git a/src/controller/site/Site.java b/src/playlist/version1/Site.java similarity index 98% rename from src/controller/site/Site.java rename to src/playlist/version1/Site.java index 2cc24419..876fc30b 100644 --- a/src/controller/site/Site.java +++ b/src/playlist/version1/Site.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.site; +package playlist.version1; import java.util.ArrayList; @@ -27,6 +27,7 @@ import controller.channel.Channel; @XmlSeeAlso( { Channel.class } ) @XmlRootElement( name = "site" ) +@Deprecated public class Site { private String mName; diff --git a/src/controller/system/System.java b/src/playlist/version1/System.java similarity index 97% rename from src/controller/system/System.java rename to src/playlist/version1/System.java index e88432f9..548bff7d 100644 --- a/src/controller/system/System.java +++ b/src/playlist/version1/System.java @@ -15,7 +15,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.system; +package playlist.version1; import java.util.ArrayList; @@ -23,10 +23,9 @@ import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; -import controller.site.Site; - @XmlSeeAlso( { Site.class } ) @XmlRootElement( name = "system" ) +@Deprecated public class System { private String mName; diff --git a/src/controller/system/SystemList.java b/src/playlist/version1/SystemList.java similarity index 94% rename from src/controller/system/SystemList.java rename to src/playlist/version1/SystemList.java index 91fcacec..5543c078 100644 --- a/src/controller/system/SystemList.java +++ b/src/playlist/version1/SystemList.java @@ -15,16 +15,17 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package controller.system; +package playlist.version1; import java.util.ArrayList; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; -@XmlSeeAlso( { controller.system.System.class } ) +@XmlSeeAlso( { playlist.version1.System.class } ) @XmlRootElement( name = "system_list" ) +@Deprecated public class SystemList { private ArrayList mSystem = new ArrayList(); diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java index 63aabd9c..fdba9a08 100644 --- a/src/source/tuner/TunerNode.java +++ b/src/source/tuner/TunerNode.java @@ -51,12 +51,10 @@ public class TunerNode extends BaseNode implements IFrequencyChangeProcessor private int mSampleRate; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; - private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; public TunerNode( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, SettingsManager settingsManager, Tuner tuner ) { @@ -64,7 +62,6 @@ public class TunerNode extends BaseNode implements IFrequencyChangeProcessor mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; mSettingsManager = settingsManager; tuner.addFrequencyChangeProcessor( this ); @@ -130,8 +127,7 @@ public class TunerNode extends BaseNode implements IFrequencyChangeProcessor public void actionPerformed( ActionEvent e ) { SpectrumFrame frame = new SpectrumFrame( getModel(), mChannelModel, - mChannelProcessingManager, mPlaylistManager, - mSettingsManager, getTuner() ); + mChannelProcessingManager, mSettingsManager, getTuner() ); } } ); diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 1209236e..0ef337c0 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -53,7 +53,6 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import properties.SystemProperties; import sample.Listener; import sample.SampleType; @@ -118,7 +117,6 @@ public class SpectralDisplayPanel extends JPanel private ConfigurationControllerModel mControllerModel; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; - private PlaylistManager mPlaylistManager; private SettingsManager mSettingsManager; private Tuner mTuner; @@ -137,13 +135,11 @@ public class SpectralDisplayPanel extends JPanel public SpectralDisplayPanel( ConfigurationControllerModel controllerModel, ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, SettingsManager settingsManager ) { mControllerModel = controllerModel; mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; - mPlaylistManager = playlistManager; mSettingsManager = settingsManager; mSpectrumPanel = new SpectrumPanel( mSettingsManager ); @@ -200,7 +196,6 @@ public class SpectralDisplayPanel extends JPanel } mControllerModel = null; - mPlaylistManager = null; mSettingsManager = null; mDFTProcessor.dispose(); @@ -657,7 +652,7 @@ public class SpectralDisplayPanel extends JPanel for( Channel channel: channels ) { JMenu channelMenu = ChannelUtils.getContextMenu( mChannelModel, - mChannelProcessingManager, mPlaylistManager, channel, + mChannelProcessingManager, channel, SpectralDisplayPanel.this ); if( channelMenu != null ) diff --git a/src/spectrum/SpectrumFrame.java b/src/spectrum/SpectrumFrame.java index f3e6eac4..a094be24 100644 --- a/src/spectrum/SpectrumFrame.java +++ b/src/spectrum/SpectrumFrame.java @@ -24,7 +24,6 @@ import java.awt.event.WindowListener; import javax.swing.JFrame; import net.miginfocom.swing.MigLayout; -import playlist.PlaylistManager; import settings.SettingsManager; import source.tuner.Tuner; import controller.ConfigurationControllerModel; @@ -40,7 +39,6 @@ public class SpectrumFrame extends JFrame implements WindowListener public SpectrumFrame( ConfigurationControllerModel controllerModel, ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - PlaylistManager playlistManager, SettingsManager settingsManager, Tuner tuner ) { @@ -51,7 +49,7 @@ public class SpectrumFrame extends JFrame implements WindowListener setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); mSpectralDisplayPanel = new SpectralDisplayPanel( controllerModel, - channelModel, channelProcessingManager, playlistManager, settingsManager ); + channelModel, channelProcessingManager, settingsManager ); mSpectralDisplayPanel.tunerSelected( tuner ); add( mSpectralDisplayPanel, "grow" ); From b6e4c4e7aa425df00ec3a0da3c7d15ba9391c688 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 6 Mar 2016 19:22:54 -0500 Subject: [PATCH 49/74] WIP #34 - new channel map manager/editor is complete. Channel map manager is now a separate window launched from the decoder tab, specifically the MPT1327 decoder tab. --- src/controller/channel/map/ChannelMap.java | 78 ++-- .../channel/map/ChannelMapEditor.java | 357 +++++++++++------- .../channel/map/ChannelMapManager.java | 96 ++++- .../channel/map/ChannelMapManagerFrame.java | 19 + src/controller/channel/map/ChannelRange.java | 74 +++- .../channel/map/ChannelRangeModel.java | 196 ++++++++-- 6 files changed, 619 insertions(+), 201 deletions(-) diff --git a/src/controller/channel/map/ChannelMap.java b/src/controller/channel/map/ChannelMap.java index 3ec928a0..a7359a16 100644 --- a/src/controller/channel/map/ChannelMap.java +++ b/src/controller/channel/map/ChannelMap.java @@ -18,17 +18,20 @@ package controller.channel.map; import java.util.ArrayList; +import java.util.List; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlSeeAlso; +import javax.xml.bind.annotation.XmlTransient; @XmlSeeAlso( { ChannelRange.class } ) public class ChannelMap { private String mName; + private boolean mInvalid = false; - private ArrayList mRanges = new ArrayList(); + private List mRanges = new ArrayList(); public ChannelMap() { @@ -39,17 +42,10 @@ public class ChannelMap { mName = name; } - - public String toString() - { - return mName; - } - /** - * Creates a copy of this channel map - */ + public ChannelMap copyOf() { - ChannelMap map = new ChannelMap( mName ); + ChannelMap map = new ChannelMap( new String( mName ) ); for( ChannelRange range: mRanges ) { @@ -58,29 +54,34 @@ public class ChannelMap return map; } - - public int getInvalidRange() + + @XmlTransient + public boolean isInvalid() { - for( int x = 0; x < mRanges.size(); x++ ) - { - if( !mRanges.get( x ).isValid() ) - { - return x; - } - } - - return -1; + return mInvalid; + } + + public String toString() + { + return mName + ( mInvalid ? " - Error" : "" ); } - public ArrayList getRanges() + public List getRanges() { return mRanges; } @XmlElement( name = "range" ) - public void setRanges( ArrayList ranges ) + public void setRanges( List ranges ) { - mRanges = ranges; + mRanges.clear(); + + for( ChannelRange range: ranges ) + { + mRanges.add( range ); + } + + validate(); } @XmlAttribute @@ -106,15 +107,44 @@ public class ChannelMap return 0; } + + /** + * Validates each of the channel ranges for overlap + */ + private void validate() + { + mInvalid = false; + + for( int x = 0; x < mRanges.size(); x++ ) + { + if( !mRanges.get( x ).isValid() ) + { + mInvalid = true; + } + + for( int y = x + 1; y < mRanges.size(); y++ ) + { + if( mRanges.get( x ).overlaps( mRanges.get( y ) ) ) + { + mInvalid = true; + } + } + } + } + public void addRange( ChannelRange range ) { mRanges.add( range ); + + validate(); } public void deleteRange( ChannelRange range ) { mRanges.remove( range ); + + validate(); } } diff --git a/src/controller/channel/map/ChannelMapEditor.java b/src/controller/channel/map/ChannelMapEditor.java index 85e39357..b79a220f 100644 --- a/src/controller/channel/map/ChannelMapEditor.java +++ b/src/controller/channel/map/ChannelMapEditor.java @@ -28,6 +28,8 @@ import javax.swing.AbstractCellEditor; import javax.swing.JButton; import javax.swing.JFormattedTextField; import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; @@ -41,8 +43,11 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.channel.map.ChannelMapEvent.Event; +import controller.channel.map.ChannelRangeModel.ChannelRangeEventListener; + public class ChannelMapEditor extends DocumentListenerEditor - implements ListSelectionListener + implements ListSelectionListener, ChannelRangeEventListener { private static final long serialVersionUID = 1L; private final static Logger mLog = @@ -52,14 +57,19 @@ public class ChannelMapEditor extends DocumentListenerEditor private JTextField mNameText; private ChannelRangeModel mRangeModel; private JTable mRangeTable; - private JButton mDelete; + private JButton mNewButton; + private JButton mDeleteButton; + private JButton mSaveButton; + private JLabel mRangeDescription; + private ChannelMapModel mChannelMapModel; - public ChannelMapEditor() + public ChannelMapEditor( ChannelMapModel channelMapModel ) { - initGUI(); + mChannelMapModel = channelMapModel; + init(); } - private ChannelMap getChannelMap() + public ChannelMap getChannelMap() { if( hasItem() ) { @@ -76,24 +86,49 @@ public class ChannelMapEditor extends DocumentListenerEditor if( hasItem() ) { + ChannelMap map = getChannelMap(); + mNameText.setText( map.getName() ); + + mRangeModel.clear(); + mRangeModel.addRanges( map.getRanges() ); + + mNewButton.setEnabled( true ); } + else + { + mNameText.setText( "" ); + + mRangeModel.clear(); + + mNewButton.setEnabled( false ); + } + + setModified( false ); } - private void initGUI() + @Override + public void setModified( boolean modified ) { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow][][]" ) ); + super.setModified( modified ); + + mSaveButton.setEnabled( modified ); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", + "[grow,fill][grow,fill][grow,fill]", "[][grow][][]" ) ); - add( new JLabel( "Channel Map" ), "span, align center" ); - - mNameLabel = new JLabel( "Name:" ); + mNameLabel = new JLabel( "Channel Map:" ); add( mNameLabel ); mNameText = new JTextField(); mNameText.getDocument().addDocumentListener( this ); - add( mNameText, "growx, push" ); + add( mNameText, "span" ); mRangeModel = new ChannelRangeModel(); + mRangeModel.setListener( this ); mRangeTable = new JTable( mRangeModel ); mRangeTable.setPreferredScrollableViewportSize( new Dimension( 300, 150 ) ); @@ -104,143 +139,201 @@ public class ChannelMapEditor extends DocumentListenerEditor mRangeTable.getColumnModel().getColumn( x ) .setCellEditor( new RangeTableEditor() ); } + + mRangeTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + int index = event.getFirstIndex(); + + mDeleteButton.setEnabled( index >= 0 ); + + if( index >= 0 ) + { + int modelIndex = mRangeTable.convertRowIndexToModel( index ); + + if( modelIndex >= 0 ) + { + ChannelRange selected = mRangeModel.getChannelRange( modelIndex ); + + if( selected != null ) + { + mRangeDescription.setText( selected.getDescription() ); + } + else + { + mRangeDescription.setText( "" ); + } + } + } + } + } + } ); JScrollPane scrollPane = new JScrollPane( mRangeTable ); add( scrollPane, "span,grow,push"); - JButton btnNew = new JButton( "New Range" ); -// btnNew.addActionListener( ChannelMapEditor.this ); - add( btnNew ); - - mDelete = new JButton( "Delete" ); -// mDelete.addActionListener( ChannelMapEditor.this ); - add( mDelete, "wrap" ); - - JButton btnSave = new JButton( "Save" ); -// btnSave.addActionListener( ChannelMapEditor.this ); - add( btnSave, "growx" ); - - JButton btnReset = new JButton( "Reset " ); -// btnReset.addActionListener( ChannelMapEditor.this ); - add( btnReset, "wrap" ); - } - - private boolean checkForValidRanges() - { - mLog.error( "Fix Me!" ); -// int invalidRow = mChannelMapCopy.getInvalidRange(); -// -// if( invalidRow != -1 && invalidRow <= mRangeTable.getRowCount() ) -// { -// mRangeTable.setRowSelectionInterval( invalidRow, invalidRow ); -// -// JOptionPane.showMessageDialog( this, -// "Starting channel number must be less than ending channel number", -// "Channel number range error", -// JOptionPane.WARNING_MESSAGE ); -// -// return false; -// } + mRangeDescription = new JLabel( " " ); + add( mRangeDescription, "span" ); - return true; - } - -// @Override -// public void actionPerformed( ActionEvent e ) -// { -// String command = e.getActionCommand(); -// -// switch( command ) -// { -// case "New Range": -// mRangeModel.addRange( new ChannelRange() ); -// break; -// case "Save": -// if( checkForValidRanges() ) -// { -// mChannelMapNode.getChannelMap() -// .setName( mChannelMapCopy.getName() ); -// mChannelMapNode.getChannelMap() -// .setRanges( mChannelMapCopy.getRanges() ); -// -// mChannelMapNode.save(); -// -// mChannelMapNode.refresh(); -// -// mChannelMapNode.show(); -// } -// case "Reset": -// mChannelMapCopy = mChannelMapNode.getChannelMap(); -// mRangeModel.setChannelMap( mChannelMapCopy ); -// mNameText.setText( mChannelMapCopy.getName() ); -// break; -// case "Delete": -// int row = mRangeTable.getSelectedRow(); -// -// if( row < 0 ) -// { -// JOptionPane.showMessageDialog( this, -// "Please select a channel range to delete", -// "Select a range", -// JOptionPane.WARNING_MESSAGE ); -// } -// else -// { -// mRangeModel.removeRange( -// mRangeTable.convertRowIndexToModel( row ) ); -// } -// break; -// } -// } - - public class RangeTableEditor extends AbstractCellEditor - implements TableCellEditor, ActionListener - { - JFormattedTextField mEditor; - - public RangeTableEditor() + mSaveButton = new JButton( "Save" ); + mSaveButton.setEnabled( false ); + mSaveButton.addActionListener( new ActionListener() { - InternationalFormatter nf = new InternationalFormatter(); - nf.setMinimum( 1 ); - nf.setAllowsInvalid( false ); - - mEditor = new JFormattedTextField( nf ); - } - - @Override - public Object getCellEditorValue() - { - return mEditor.getText(); - } + @Override + public void actionPerformed( ActionEvent e ) + { + save(); + } + } ); + add( mSaveButton ); - @Override - public void actionPerformed( ActionEvent e ) - { - } + mNewButton = new JButton( "New Range" ); + mNewButton.setEnabled( false ); + mNewButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + mRangeModel.addRange( new ChannelRange() ); + } + } ); + add( mNewButton ); - @Override - public Component getTableCellEditorComponent( JTable table, - Object value, - boolean isSelected, - int row, - int column ) - { - mEditor.setValue( value ); - - return mEditor; - } - } + mDeleteButton = new JButton( "DeleteRange" ); + mDeleteButton.setEnabled( false ); + mDeleteButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + int selectedRow = mRangeTable.getSelectedRow(); + + if( selectedRow >= 0 ) + { + int modelRow = mRangeTable.convertRowIndexToModel( selectedRow ); + ChannelRange selectedRange = mRangeModel.getChannelRange( modelRow ); + + if( selectedRange != null ) + { + int choice = JOptionPane.showConfirmDialog( mRangeTable, + "Do you want to delete this channel range?", + "Delete Channel Range?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + mRangeModel.removeRange( selectedRange ); + mDeleteButton.setEnabled( false ); + } + } + } + } + } ); + add( mDeleteButton, "wrap" ); + } + @Override public void save() { - // TODO Auto-generated method stub - + if( isModified() && hasItem() ) + { + ChannelMap channelMap = getChannelMap(); + + channelMap.setName( mNameText.getText() ); + channelMap.setRanges( mRangeModel.getChannelRanges() ); + + mChannelMapModel.broadcast( new ChannelMapEvent( getChannelMap(), Event.CHANGE ) ); + setModified( false ); + } } @Override public void valueChanged( ListSelectionEvent e ) { - mLog.debug( "List Select Event from: " + e.getSource().getClass() ); + if( !e.getValueIsAdjusting() ) + { + if( e.getSource() instanceof JList ) + { + JList list = (JList)e.getSource(); + + if( list.getSelectedValue() instanceof ChannelMap ) + { + setItem( (ChannelMap)list.getSelectedValue() ); + } + else + { + setItem( null ); + } + } + } } + + @Override + public void channelRangesChanged() + { + setModified( true ); + + //Update description + int index = mRangeTable.getSelectedRow(); + + if( index >= 0 ) + { + ChannelRange selected = mRangeModel.getChannelRange( + mRangeTable.convertRowIndexToModel( index ) ); + + if( selected != null ) + { + mRangeDescription.setText( selected.getDescription() ); + } + else + { + mRangeDescription.setText( "" ); + } + } + } + + public class RangeTableEditor extends AbstractCellEditor + implements TableCellEditor, ActionListener + { + private static final long serialVersionUID = 1L; + + JFormattedTextField mEditor; + + public RangeTableEditor() + { + InternationalFormatter nf = new InternationalFormatter(); + nf.setMinimum( 1 ); + nf.setAllowsInvalid( true ); + + mEditor = new JFormattedTextField( nf ); + } + + @Override + public Object getCellEditorValue() + { + return mEditor.getText(); + } + + @Override + public void actionPerformed( ActionEvent e ) + { + } + + @Override + public Component getTableCellEditorComponent( JTable table, + Object value, + boolean isSelected, + int row, + int column ) + { + mEditor.setValue( value ); + + return mEditor; + } + } + } diff --git a/src/controller/channel/map/ChannelMapManager.java b/src/controller/channel/map/ChannelMapManager.java index d92626a5..78e0608e 100644 --- a/src/controller/channel/map/ChannelMapManager.java +++ b/src/controller/channel/map/ChannelMapManager.java @@ -1,9 +1,16 @@ package controller.channel.map; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; import javax.swing.JList; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import net.miginfocom.swing.MigLayout; @@ -16,30 +23,111 @@ public class ChannelMapManager extends JPanel private ChannelMapModel mChannelMapModel; private JList mChannelMapList; private ChannelMapEditor mChannelMapEditor; + private JButton mDeleteMapButton; + private JButton mCloneMapButton; public ChannelMapManager( ChannelMapModel channelMapModel ) { mChannelMapModel = channelMapModel; - mChannelMapList = new JList( mChannelMapModel ); - mChannelMapEditor = new ChannelMapEditor(); + mChannelMapList = new JList<>( mChannelMapModel ); + mChannelMapEditor = new ChannelMapEditor( mChannelMapModel ); init(); } private void init() { - setLayout( new MigLayout( "", "[fill,grow]", "[fill,grow]" ) ); + setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) ); + JPanel listPanel = new JPanel(); + listPanel.setLayout( new MigLayout( "", + "[fill,grow][fill,grow][fill,grow]", "[fill,grow][]" ) ); + mChannelMapList.addListSelectionListener( mChannelMapEditor ); + mChannelMapList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent e ) + { + if( mChannelMapList.getSelectedValue() != null ) + { + mCloneMapButton.setEnabled( true ); + mDeleteMapButton.setEnabled( true ); + } + else + { + mCloneMapButton.setEnabled( false ); + mDeleteMapButton.setEnabled( false ); + } + } + } ); mChannelMapList.setLayoutOrientation( JList.VERTICAL ); mChannelMapList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + JScrollPane listScroller = new JScrollPane( mChannelMapList ); + + listPanel.add( listScroller, "span" ); + + JButton newMapButton = new JButton( "New" ); + newMapButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + ChannelMap channelMap = new ChannelMap( "New Channel Map" ); + mChannelMapModel.addChannelMap( channelMap ); + mChannelMapList.setSelectedValue( channelMap, true ); + } + } ); + + listPanel.add( newMapButton ); + + mCloneMapButton = new JButton( "Clone" ); + mCloneMapButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + ChannelMap selected = mChannelMapList.getSelectedValue(); + + if( selected != null ) + { + ChannelMap copy = selected.copyOf(); + mChannelMapModel.addChannelMap( copy ); + mChannelMapList.setSelectedValue( copy, true ); + } + } + } ); + listPanel.add( mCloneMapButton ); + + mDeleteMapButton = new JButton( "Delete" ); + mDeleteMapButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + ChannelMap channelMap = mChannelMapList.getSelectedValue(); + + if( channelMap != null ) + { + int choice = JOptionPane.showConfirmDialog( mChannelMapList, + "Do you want to delete this channel map?", + "Delete Channel Map?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + mChannelMapModel.removeChannelMap( channelMap ); + } + } + } + } ); + listPanel.add( mDeleteMapButton ); JScrollPane editorScroller = new JScrollPane( mChannelMapEditor ); JideSplitPane splitPane = new JideSplitPane(); splitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT ); - splitPane.add( listScroller ); + splitPane.add( listPanel ); splitPane.add( editorScroller ); add( splitPane ); diff --git a/src/controller/channel/map/ChannelMapManagerFrame.java b/src/controller/channel/map/ChannelMapManagerFrame.java index c3c48628..04812979 100644 --- a/src/controller/channel/map/ChannelMapManagerFrame.java +++ b/src/controller/channel/map/ChannelMapManagerFrame.java @@ -17,6 +17,8 @@ ******************************************************************************/ package controller.channel.map; +import java.awt.EventQueue; + import javax.swing.JFrame; import net.miginfocom.swing.MigLayout; @@ -39,4 +41,21 @@ public class ChannelMapManagerFrame extends JFrame add( channelMapManager ); setLocationRelativeTo( null ); } + + public static void main( String[] args ) + { + ChannelMapModel channelMapModel = new ChannelMapModel(); + + final ChannelMapManagerFrame frame = + new ChannelMapManagerFrame( channelMapModel ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + frame.setVisible( true ); + } + } ); + } } diff --git a/src/controller/channel/map/ChannelRange.java b/src/controller/channel/map/ChannelRange.java index a57ee199..28b9a5b4 100644 --- a/src/controller/channel/map/ChannelRange.java +++ b/src/controller/channel/map/ChannelRange.java @@ -17,21 +17,35 @@ ******************************************************************************/ package controller.channel.map; +import java.text.DecimalFormat; + import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlTransient; import javax.xml.bind.annotation.XmlType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + @XmlRootElement @XmlType( name = "range" ) public class ChannelRange { + private final static Logger mLog = LoggerFactory.getLogger( ChannelRange.class ); + + private static final DecimalFormat FREQUENCY_FORMATTER = + new DecimalFormat( "#.0000" ); + private int mFirst = 1; private int mLast = 1023; private int mBase = 450000000; private int mSize = 12500; + + private boolean mOverlapping = false; - /* JAXB requirement - do not remove */ - public ChannelRange() {} + public ChannelRange() + { + } public ChannelRange( int first, int last, int base, int size ) { @@ -46,11 +60,67 @@ public class ChannelRange return new ChannelRange( mFirst, mLast, mBase, mSize ); } + public String getDescription() + { + StringBuilder sb = new StringBuilder(); + + if( isValid() ) + { + sb.append( "First: " ); + sb.append( mFirst + "=" ); + + long frequency = getFrequency( mFirst ); + + sb.append( FREQUENCY_FORMATTER.format( (double)frequency / 1E6D ) ); + sb.append( " Last: " ); + sb.append( mLast + "=" ); + + frequency = getFrequency( mLast ); + sb.append( FREQUENCY_FORMATTER.format( (double)frequency / 1E6D ) ); + sb.append( " MHz" ); + } + else + { + sb.append( "First channel must be smaller than last channel" ); + } + + if( isOverlapping() ) + { + if( sb.length() > 0 ) + { + sb.append( ", " ); + } + + sb.append( "Overlap!" ); + } + + return sb.toString(); + } + public boolean isValid() { return mFirst < mLast; } + public boolean overlaps( ChannelRange other ) + { + return ( mFirst <= other.mFirst && other.mFirst <= mLast ) || + ( mFirst <= other.mLast && other.mLast <= mLast ) || + ( mFirst <= other.mFirst && other.mLast <= mLast ) || + ( other.mFirst <= mFirst && mLast <= other.mLast ); + } + + @XmlTransient + public boolean isOverlapping() + { + return mOverlapping; + } + + public void setOverlapping( boolean overlapping ) + { + mOverlapping = overlapping; + } + public boolean hasChannel( int channel ) { return mFirst <= channel && channel <= mLast; diff --git a/src/controller/channel/map/ChannelRangeModel.java b/src/controller/channel/map/ChannelRangeModel.java index 9e13d345..d5269c1d 100644 --- a/src/controller/channel/map/ChannelRangeModel.java +++ b/src/controller/channel/map/ChannelRangeModel.java @@ -17,6 +17,11 @@ ******************************************************************************/ package controller.channel.map; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + import javax.swing.table.AbstractTableModel; import org.slf4j.Logger; @@ -32,49 +37,91 @@ public class ChannelRangeModel extends AbstractTableModel private static final int STOP = 1; private static final int BASE = 2; private static final int SIZE = 3; + private static final int ERROR = 4; - protected int[] mColumnWidths = { 110, 110, 110, -1 }; + protected int[] mColumnWidths = { 110, 110, 110, -1, 80 }; protected String[] mHeaders = new String[] { "Begin", "End", "Base", - "Size" }; + "Size", + "Error" }; - private ChannelMap mChannelMap; + private List mRanges = new CopyOnWriteArrayList<>(); + private ChannelRangeEventListener mListener; public ChannelRangeModel() { } - public void setChannelMap( ChannelMap channelMap ) + public void setListener( ChannelRangeEventListener listener ) { - mChannelMap = channelMap; - fireTableDataChanged(); + mListener = listener; + } + + private void broadcastChange() + { + if( mListener != null ) + { + mListener.channelRangesChanged(); + } + } + + public void clear() + { + for( ChannelRange range: mRanges ) + { + removeRange( range ); + } + + broadcastChange(); + } + + public List getChannelRanges() + { + return Collections.unmodifiableList( mRanges ); + } + + public void addRanges( List ranges ) + { + for( ChannelRange range: ranges ) + { + addRange( range ); + } + + broadcastChange(); } public void addRange( ChannelRange range ) { - if( mChannelMap != null ) + if( range != null && !mRanges.contains( range ) ) { - mChannelMap.addRange( range ); - - int index = mChannelMap.getRanges().size() - 1; - + mRanges.add( range ); + + int index = mRanges.indexOf( range ); + fireTableRowsInserted( index, index ); } + + validate(); + + broadcastChange(); } - public void removeRange( int rangeIndex ) + public void removeRange( ChannelRange range ) { - if( mChannelMap != null ) + if( range != null && mRanges.contains( range ) ) { - if( rangeIndex < mChannelMap.getRanges().size() ) - { - mChannelMap.getRanges().remove( rangeIndex ); - } + int index = mRanges.indexOf( range ); - fireTableRowsDeleted( rangeIndex, rangeIndex ); + mRanges.remove( range ); + + fireTableRowsDeleted( index, index ); } + + validate(); + + broadcastChange(); } public int[] getColumnWidths() @@ -84,10 +131,11 @@ public class ChannelRangeModel extends AbstractTableModel public void setColumnWidths( int[] widths ) { - if( widths.length != 4 ) + if( widths.length != mHeaders.length ) { throw new IllegalArgumentException( "ChannelMapRangeModel - " - + "column widths array should have 5 elements" ); + + "column widths array should have " + + mHeaders.length + " elements" ); } else { @@ -98,12 +146,7 @@ public class ChannelRangeModel extends AbstractTableModel @Override public int getRowCount() { - if( mChannelMap != null ) - { - return mChannelMap.getRanges().size(); - } - - return 0; + return mRanges.size(); } @Override @@ -117,13 +160,23 @@ public class ChannelRangeModel extends AbstractTableModel return mHeaders[ column ]; } + public ChannelRange getChannelRange( int index ) + { + if( index < mRanges.size() ) + { + return mRanges.get( index ); + } + + return null; + } + @Override public Object getValueAt( int rowIndex, int columnIndex ) { - if( mChannelMap != null ) + ChannelRange range = getChannelRange( rowIndex ); + + if( range != null ) { - ChannelRange range = mChannelMap.getRanges().get( rowIndex ); - switch( columnIndex ) { case START: @@ -134,27 +187,84 @@ public class ChannelRangeModel extends AbstractTableModel return range.getBase(); case SIZE: return range.getSize(); + case ERROR: + StringBuilder sb = new StringBuilder(); + + if( !range.isValid() ) + { + sb.append( "Sequence" ); + } + + if( range.isOverlapping() ) + { + if( sb.length() > 0 ) + { + sb.append( "," ); + } + + sb.append( "Overlap" ); + } + + return sb.toString(); + default: + break; } } return null; } + + /** + * Validates each of the channel ranges for overlap + */ + private void validate() + { + for( int x = 0; x < mRanges.size(); x++ ) + { + mRanges.get( x ).setOverlapping( false ); + fireTableCellUpdated( x, ERROR ); + } + + for( int x = 0; x < mRanges.size(); x++ ) + { + for( int y = x + 1; y < mRanges.size(); y++ ) + { + if( mRanges.get( x ).overlaps( mRanges.get( y ) ) ) + { + mRanges.get( x ).setOverlapping( true ); + fireTableCellUpdated( x, ERROR ); + + mRanges.get( y ).setOverlapping( true ); + fireTableCellUpdated( y, ERROR ); + } + } + } + } public boolean isCellEditable( int row, int column ) { - return mChannelMap != null; + return row < mRanges.size() && column != ERROR; } public void setValueAt( Object value, int row, int column ) { - if( mChannelMap != null ) + int parsedValue = -1; + + try { - try + parsedValue = Integer.parseInt( (String)value ); + } + catch( Exception e ) + { + //Do nothing ... we'll use 0 + } + + if( parsedValue >= 0 ) + { + ChannelRange range = getChannelRange( row ); + + if( range != null ) { - int parsedValue = Integer.parseInt( (String)value ); - - ChannelRange range = mChannelMap.getRanges().get( row ); - switch( column ) { case START: @@ -170,12 +280,20 @@ public class ChannelRangeModel extends AbstractTableModel range.setSize( parsedValue ); break; } + + validate(); + + broadcastChange(); } - catch( Exception e ) + else { - mLog.error( "ChannelMapModel - couldn't parse integer value " - + "for [" + value + "]", e ); + throw new IllegalArgumentException( "Invalid channel range row index" ); } } } + + public interface ChannelRangeEventListener + { + public void channelRangesChanged(); + } } From 9273f679f4634a60f50f38b08e7e1563194be1ef Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 7 Mar 2016 19:05:34 -0500 Subject: [PATCH 50/74] Resolves issue with loading of channel map ranges from playlist_v2.xml --- src/controller/channel/map/ChannelMap.java | 11 +++-------- src/controller/channel/map/ChannelRangeModel.java | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/controller/channel/map/ChannelMap.java b/src/controller/channel/map/ChannelMap.java index a7359a16..107280f8 100644 --- a/src/controller/channel/map/ChannelMap.java +++ b/src/controller/channel/map/ChannelMap.java @@ -66,20 +66,15 @@ public class ChannelMap return mName + ( mInvalid ? " - Error" : "" ); } + @XmlElement( name = "range" ) public List getRanges() { return mRanges; } - @XmlElement( name = "range" ) public void setRanges( List ranges ) { - mRanges.clear(); - - for( ChannelRange range: ranges ) - { - mRanges.add( range ); - } + mRanges = ranges; validate(); } @@ -140,7 +135,7 @@ public class ChannelMap validate(); } - public void deleteRange( ChannelRange range ) + public void removeRange( ChannelRange range ) { mRanges.remove( range ); diff --git a/src/controller/channel/map/ChannelRangeModel.java b/src/controller/channel/map/ChannelRangeModel.java index d5269c1d..f2be227f 100644 --- a/src/controller/channel/map/ChannelRangeModel.java +++ b/src/controller/channel/map/ChannelRangeModel.java @@ -79,7 +79,7 @@ public class ChannelRangeModel extends AbstractTableModel public List getChannelRanges() { - return Collections.unmodifiableList( mRanges ); + return mRanges; } public void addRanges( List ranges ) From 6fa3089bc9624e72f17247677de7584de2b00e12 Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 7 Mar 2016 20:43:43 -0500 Subject: [PATCH 51/74] WIP #34 - adds tuner model and view tab, preparing to remove the configuration panel from the controller view. --- src/controller/ControllerPanel.java | 10 ++- src/gui/SDRTrunk.java | 6 +- src/source/SampleProvider.java | 28 --------- src/source/TunerModel.java | 88 +++++++++++++++++++++++++++ src/source/TunerViewPanel.java | 94 +++++++++++++++++++++++++++++ src/source/tuner/TunerManager.java | 18 ++++++ 6 files changed, 214 insertions(+), 30 deletions(-) delete mode 100644 src/source/SampleProvider.java create mode 100644 src/source/TunerModel.java create mode 100644 src/source/TunerViewPanel.java diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index c9018a7c..79d24625 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -35,6 +35,8 @@ import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import source.SourceManager; +import source.TunerViewPanel; +import source.TunerModel; import spectrum.ChannelSpectrumPanel; import alias.AliasController; import alias.AliasModel; @@ -55,6 +57,8 @@ public class ControllerPanel extends JPanel private ChannelList mChannelStateList; + private TunerViewPanel mTunerManagerPanel; + private CallEventPanel mCallEventPanel; private MessageActivityPanel mMessageActivityPanel; @@ -87,7 +91,8 @@ public class ControllerPanel extends JPanel ChannelProcessingManager channelProcessingManager, MapService mapService, SettingsManager settingsManager, - SourceManager sourceManager ) + SourceManager sourceManager, + TunerModel tunerModel ) { mChannelModel = channelModel; mController = controller; @@ -111,6 +116,8 @@ public class ControllerPanel extends JPanel sourceManager, aliasModel ); mAliasController = new AliasController( aliasModel, mSettingsManager ); + + mTunerManagerPanel = new TunerViewPanel( tunerModel, mSettingsManager ); init(); } @@ -138,6 +145,7 @@ public class ControllerPanel extends JPanel mTabbedPane.setForeground( Color.BLACK ); mTabbedPane.addTab( "Channels", mChannelController ); mTabbedPane.addTab( "Aliases", mAliasController ); + mTabbedPane.addTab( "Tuners", mTunerManagerPanel ); mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 505f0dd9..16658a8f 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -56,6 +56,7 @@ import properties.SystemProperties; import record.RecorderManager; import settings.SettingsManager; import source.SourceManager; +import source.TunerModel; import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import spectrum.SpectralDisplayPanel; @@ -130,6 +131,9 @@ public class SDRTrunk SourceManager sourceManager = new SourceManager( mSettingsManager, threadPoolManager ); + + TunerModel tunerModel = new TunerModel(); + tunerModel.addTuners( sourceManager.getTunerManager().getTuners() ); ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager( channelModel, channelMapModel, @@ -158,7 +162,7 @@ public class SDRTrunk mControllerPanel = new ControllerPanel( audioManager, configurationControllerModel, aliasModel, channelModel, channelMapModel, channelProcessingManager, - mapService, mSettingsManager, sourceManager ); + mapService, mSettingsManager, sourceManager, tunerModel ); mSpectralPanel = new SpectralDisplayPanel( configurationControllerModel, channelModel, channelProcessingManager, mSettingsManager ); diff --git a/src/source/SampleProvider.java b/src/source/SampleProvider.java deleted file mode 100644 index 3cc84749..00000000 --- a/src/source/SampleProvider.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source; - -import sample.SampleType; - -public interface SampleProvider -{ - /** - * Real or Complex - */ - public abstract SampleType getSampleType(); -} diff --git a/src/source/TunerModel.java b/src/source/TunerModel.java new file mode 100644 index 00000000..dd51250e --- /dev/null +++ b/src/source/TunerModel.java @@ -0,0 +1,88 @@ +package source; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.AbstractListModel; + +import source.tuner.Tuner; + +public class TunerModel extends AbstractListModel +{ + private static final long serialVersionUID = 1L; + + private List mTuners = new ArrayList<>(); + + public TunerModel() + { + } + + /** + * List of Tuners currently in the model + */ + public List getTuners() + { + return mTuners; + } + + public void addTuners( List Tuners ) + { + for( Tuner tuner: Tuners ) + { + addTuner( tuner ); + } + } + + /** + * Adds the Tuner to this model + */ + public void addTuner( Tuner tuner ) + { + if( !mTuners.contains( tuner ) ) + { + mTuners.add( tuner ); + + int index = mTuners.indexOf( tuner ); + + fireIntervalAdded( this, index, index ); + } + } + + /** + * Removes the Tuner from this model + */ + public void removeTuner( Tuner tuner ) + { + if( mTuners.contains( tuner ) ) + { + int index = mTuners.indexOf( tuner ); + + mTuners.remove( tuner ); + + fireIntervalRemoved( this, index, index ); + } + } + + /** + * Number of Tuners in this model + */ + @Override + public int getSize() + { + return mTuners.size(); + } + + /** + * Returns the Tuner at the specified index + */ + @Override + public Tuner getElementAt( int index ) + { + if( index <= mTuners.size() ) + { + return mTuners.get( index ); + } + + return null; + } +} diff --git a/src/source/TunerViewPanel.java b/src/source/TunerViewPanel.java new file mode 100644 index 00000000..e2b9ef57 --- /dev/null +++ b/src/source/TunerViewPanel.java @@ -0,0 +1,94 @@ +package source; + +import gui.editor.EmptyEditor; + +import java.awt.Component; + +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ListSelectionModel; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import settings.SettingsManager; +import source.tuner.Tuner; +import source.tuner.TunerManager; + +import com.jidesoft.swing.JideSplitPane; + +public class TunerViewPanel extends JPanel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( TunerViewPanel.class ); + + private TunerModel mTunerModel; + private SettingsManager mSettingsManager; + private JideSplitPane mSplitPane; + private JList mTunerList; + + public TunerViewPanel( TunerModel tunerModel, SettingsManager settingsManager ) + { + mTunerModel = tunerModel; + mSettingsManager = settingsManager; + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) ); + + mTunerList = new JList<>( mTunerModel ); + mTunerList.setLayoutOrientation( JList.VERTICAL ); + mTunerList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mTunerList.addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + Tuner selected = mTunerList.getSelectedValue(); + + Component component = null; + + if( selected != null ) + { + JPanel tunerEditor = TunerManager + .getEditor( mSettingsManager, selected ); + + component = new JScrollPane( tunerEditor ); + } + else + { + component = new EmptyEditor(); + } + + int split = mSplitPane.getDividerLocation( 0 ); + + mSplitPane.remove( 2 ); + mSplitPane.add( component ); + mSplitPane.setDividerLocation( 0, split ); + validate(); + } + } + } ); + + JScrollPane listScroller = new JScrollPane( mTunerList ); + + JScrollPane editorScroller = new JScrollPane( new EmptyEditor() ); + + mSplitPane = new JideSplitPane(); + mSplitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT ); + mSplitPane.add( listScroller ); + mSplitPane.add( editorScroller ); + + add( mSplitPane ); + } +} diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index 759bf7d6..38b10e0a 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -17,12 +17,16 @@ ******************************************************************************/ package source.tuner; +import gui.editor.EmptyEditor; + import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import javax.swing.JPanel; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.Device; @@ -38,6 +42,7 @@ import source.config.SourceConfiguration; import source.mixer.MixerManager; import source.tuner.airspy.AirspyTuner; import source.tuner.airspy.AirspyTunerController; +import source.tuner.airspy.AirspyTunerEditorPanel; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerController; import source.tuner.fcd.proplusV2.FCD2TunerController; @@ -46,6 +51,7 @@ import source.tuner.hackrf.HackRFTunerController; import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; +import source.tuner.rtl.e4k.E4KTunerEditorPanel; import source.tuner.rtl.r820t.R820TTunerController; import controller.ThreadPoolManager; @@ -618,4 +624,16 @@ public class TunerManager return mTuner != null; } } + + public static JPanel getEditor( SettingsManager settingsManager, Tuner tuner ) + { + JPanel editor = tuner.getEditor( settingsManager ); + + if( editor != null ) + { + return editor; + } + + return new EmptyEditor(); + } } From 9d4afb8c1f1438704d217f8535900de56ccb2c36 Mon Sep 17 00:00:00 2001 From: denny Date: Fri, 11 Mar 2016 07:16:29 -0500 Subject: [PATCH 52/74] WIP #34 - updates tuners and tuner model to provide details on frequency, sample rate, sourced channels, and spectral display in the tuner view tab. --- src/controller/ControllerPanel.java | 30 +++--- src/source/TunerModel.java | 126 ++++++++++++++++++++--- src/source/TunerViewPanel.java | 23 +++-- src/source/tuner/Tuner.java | 55 +++++++++- src/source/tuner/TunerChangeEvent.java | 33 ++++++ src/source/tuner/TunerController.java | 5 + src/source/tuner/TunerType.java | 2 +- src/source/tuner/airspy/AirspyTuner.java | 18 +++- src/source/tuner/ettus/B100Tuner.java | 7 ++ src/source/tuner/fcd/FCDTuner.java | 16 ++- src/source/tuner/hackrf/HackRFTuner.java | 16 ++- src/source/tuner/rtl/RTL2832Tuner.java | 13 +++ 12 files changed, 296 insertions(+), 48 deletions(-) create mode 100644 src/source/tuner/TunerChangeEvent.java diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index 79d24625..bcb21c13 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -71,9 +71,9 @@ public class ControllerPanel extends JPanel private JideTabbedPane mTabbedPane; - protected ConfigurationTreePanel mSystemControlViewPanel; - protected OldConfigurationEditor mConfigurationEditor; - protected JideSplitPane mSystemControlSplitPane; +// protected ConfigurationTreePanel mSystemControlViewPanel; +// protected OldConfigurationEditor mConfigurationEditor; +// protected JideSplitPane mSystemControlSplitPane; protected JTable mChannelActivityTable = new JTable(); private AudioPanel mAudioPanel; @@ -128,25 +128,25 @@ public class ControllerPanel extends JPanel "[grow,fill]", "[grow,fill]") ); - //System Configuration View and Editor - mConfigurationEditor = new OldConfigurationEditor(); +// //System Configuration View and Editor +// mConfigurationEditor = new OldConfigurationEditor(); +// +// mSystemControlViewPanel = new ConfigurationTreePanel( mController ); +// mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor ); - mSystemControlViewPanel = new ConfigurationTreePanel( mController ); - mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor ); - - mSystemControlSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); - mSystemControlSplitPane.setDividerSize( 5 ); - mSystemControlSplitPane.add( mSystemControlViewPanel ); - mSystemControlSplitPane.add( mConfigurationEditor ); +// mSystemControlSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); +// mSystemControlSplitPane.setDividerSize( 5 ); +// mSystemControlSplitPane.add( mSystemControlViewPanel ); +// mSystemControlSplitPane.add( mConfigurationEditor ); //Tabbed View - configuration, calls, messages, map mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont( this.getFont() ); mTabbedPane.setForeground( Color.BLACK ); + mTabbedPane.addTab( "Tuners", mTunerManagerPanel ); mTabbedPane.addTab( "Channels", mChannelController ); mTabbedPane.addTab( "Aliases", mAliasController ); - mTabbedPane.addTab( "Tuners", mTunerManagerPanel ); - mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); +// mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); mTabbedPane.addTab( "Messages", mMessageActivityPanel ); diff --git a/src/source/TunerModel.java b/src/source/TunerModel.java index dd51250e..4e5655a4 100644 --- a/src/source/TunerModel.java +++ b/src/source/TunerModel.java @@ -1,17 +1,34 @@ package source; +import java.text.DecimalFormat; import java.util.ArrayList; import java.util.List; -import javax.swing.AbstractListModel; +import javax.swing.table.AbstractTableModel; +import sample.Listener; import source.tuner.Tuner; +import source.tuner.TunerChangeEvent; -public class TunerModel extends AbstractListModel +public class TunerModel extends AbstractTableModel + implements Listener { private static final long serialVersionUID = 1L; + private static final int TUNER_CLASS = 0; + private static final int TUNER_TYPE = 1; + private static final int SAMPLE_RATE = 2; + private static final int FREQUENCY = 3; + private static final int CHANNEL_COUNT = 4; + private static final int SPECTRAL_DISPLAY = 5; + + private static final String MHZ = " MHz"; + private static final String[] COLUMNS = + { "Class", "Type", "Sample Rate", "Frequency", "Channels", "Spectrum" }; + private List mTuners = new ArrayList<>(); + private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.00000" ); + private DecimalFormat mSampleRateFormat = new DecimalFormat( "0.000" ); public TunerModel() { @@ -24,6 +41,16 @@ public class TunerModel extends AbstractListModel { return mTuners; } + + public Tuner getTuner( int index ) + { + if( index < mTuners.size() ) + { + return mTuners.get( index ); + } + + return null; + } public void addTuners( List Tuners ) { @@ -44,7 +71,9 @@ public class TunerModel extends AbstractListModel int index = mTuners.indexOf( tuner ); - fireIntervalAdded( this, index, index ); + fireTableRowsInserted( index, index ); + + tuner.addTunerChangeListener( this ); } } @@ -55,34 +84,101 @@ public class TunerModel extends AbstractListModel { if( mTuners.contains( tuner ) ) { + tuner.removeTunerChangeListener( this ); + int index = mTuners.indexOf( tuner ); mTuners.remove( tuner ); - fireIntervalRemoved( this, index, index ); + fireTableRowsDeleted( index, index ); } } - /** - * Number of Tuners in this model - */ + @Override - public int getSize() + public void receive( TunerChangeEvent event ) + { + if( event.getTuner() != null ) + { + int index = mTuners.indexOf( event.getTuner() ); + + if( index >= 0 ) + { + switch( event.getEvent() ) + { + case CHANNEL_COUNT: + fireTableCellUpdated( index, CHANNEL_COUNT ); + break; + case FREQUENCY: + fireTableCellUpdated( index, FREQUENCY ); + break; + case SAMPLE_RATE: + fireTableCellUpdated( index, SAMPLE_RATE ); + break; + case SPECTRAL_DISPLAY: + fireTableCellUpdated( index, SPECTRAL_DISPLAY ); + default: + break; + } + } + } + } + + @Override + public int getRowCount() { return mTuners.size(); } - /** - * Returns the Tuner at the specified index - */ @Override - public Tuner getElementAt( int index ) + public int getColumnCount() { - if( index <= mTuners.size() ) + return 6; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + if( rowIndex < mTuners.size() ) { - return mTuners.get( index ); + Tuner tuner = mTuners.get( rowIndex ); + + switch( columnIndex ) + { + case TUNER_CLASS: + return tuner.getTunerClass().getVendorDescription(); + case TUNER_TYPE: + return tuner.getTunerType().getLabel(); + case SAMPLE_RATE: + int sampleRate = tuner.getSampleRate(); + + return mSampleRateFormat.format( sampleRate / 1E6D ) + MHZ; + case FREQUENCY: + try + { + long frequency = tuner.getFrequency(); + + return mFrequencyFormat.format( frequency / 1E6D ) + MHZ; + } + catch( Exception e ) + { + return 0; + } + case CHANNEL_COUNT: + return tuner.getChannelCount(); + case SPECTRAL_DISPLAY: + return "TBD"; + default: + break; + } } - + return null; } + + @Override + public String getColumnName( int columnIndex ) + { + return COLUMNS[ columnIndex ]; + } } diff --git a/src/source/TunerViewPanel.java b/src/source/TunerViewPanel.java index e2b9ef57..5b577bfb 100644 --- a/src/source/TunerViewPanel.java +++ b/src/source/TunerViewPanel.java @@ -3,10 +3,11 @@ package source; import gui.editor.EmptyEditor; import java.awt.Component; +import java.awt.Dimension; -import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -30,7 +31,7 @@ public class TunerViewPanel extends JPanel private TunerModel mTunerModel; private SettingsManager mSettingsManager; private JideSplitPane mSplitPane; - private JList mTunerList; + private JTable mTunerTable; public TunerViewPanel( TunerModel tunerModel, SettingsManager settingsManager ) { @@ -44,17 +45,19 @@ public class TunerViewPanel extends JPanel { setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) ); - mTunerList = new JList<>( mTunerModel ); - mTunerList.setLayoutOrientation( JList.VERTICAL ); - mTunerList.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - mTunerList.addListSelectionListener( new ListSelectionListener() + mTunerTable = new JTable( mTunerModel ); + mTunerTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + mTunerTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() { @Override public void valueChanged( ListSelectionEvent event ) { if( !event.getValueIsAdjusting() ) { - Tuner selected = mTunerList.getSelectedValue(); + int row = mTunerTable.getSelectedRow(); + int modelRow = mTunerTable.convertRowIndexToModel( row ); + + Tuner selected = mTunerModel.getTuner( modelRow ); Component component = null; @@ -80,12 +83,14 @@ public class TunerViewPanel extends JPanel } } ); - JScrollPane listScroller = new JScrollPane( mTunerList ); + JScrollPane listScroller = new JScrollPane( mTunerTable ); + listScroller.setPreferredSize( new Dimension( 400, 20 ) ); JScrollPane editorScroller = new JScrollPane( new EmptyEditor() ); + editorScroller.setPreferredSize( new Dimension( 400, 80 ) ); mSplitPane = new JideSplitPane(); - mSplitPane.setOrientation( JideSplitPane.HORIZONTAL_SPLIT ); + mSplitPane.setOrientation( JideSplitPane.VERTICAL_SPLIT ); mSplitPane.add( listScroller ); mSplitPane.add( editorScroller ); diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index e5769b54..cc4a8195 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -17,6 +17,8 @@ ******************************************************************************/ package source.tuner; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.RejectedExecutionException; @@ -32,7 +34,6 @@ import source.SourceException; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; -import controller.ThreadPoolManager; /** * Tuner - provides tuner channel sources, representing a channel frequency @@ -48,12 +49,15 @@ public abstract class Tuner implements IFrequencyChangeProcessor, * Sample Listeners - these will typically be the DFT processor for spectral * display, or it will be one or more tuner channel sources */ - protected CopyOnWriteArrayList> - mSampleListeners = new CopyOnWriteArrayList>(); + protected List> mSampleListeners = + new CopyOnWriteArrayList<>(); - protected CopyOnWriteArrayList - mFrequencyChangeProcessors = new CopyOnWriteArrayList(); + protected List mFrequencyChangeProcessors = + new CopyOnWriteArrayList<>(); + protected List> mTunerChangeListeners = + new ArrayList<>(); + public Tuner( String name ) { mName = name; @@ -148,6 +152,11 @@ public abstract class Tuner implements IFrequencyChangeProcessor, public abstract TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException; + /** + * Count of currently sourced channels (TunerChannel). + */ + public abstract int getChannelCount(); + /** * Releases the tuned channel resources * @@ -211,6 +220,33 @@ public abstract class Tuner implements IFrequencyChangeProcessor, new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, frequency ) ); } + /** + * Registers the listener + */ + public void addTunerChangeListener( Listener listener ) + { + mTunerChangeListeners.add( listener ); + } + + /** + * Removes the registered listener + */ + public void removeTunerChangeListener( Listener listener ) + { + mTunerChangeListeners.remove( listener ); + } + + /** + * Broadcasts the tuner change event + */ + public void broadcast( TunerChangeEvent event ) + { + for( Listener listener: mTunerChangeListeners ) + { + listener.receive( event ); + } + } + /** * Broadcasts a sample rate change event to all registered listeners */ @@ -238,6 +274,15 @@ public abstract class Tuner implements IFrequencyChangeProcessor, { processor.frequencyChanged( event ); } + + if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) + { + broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.FREQUENCY ) ); + } + else if( event.getEvent() == Event.NOTIFICATION_SAMPLE_RATE_CHANGE ) + { + broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.SAMPLE_RATE ) ); + } } /** diff --git a/src/source/tuner/TunerChangeEvent.java b/src/source/tuner/TunerChangeEvent.java new file mode 100644 index 00000000..72bf366c --- /dev/null +++ b/src/source/tuner/TunerChangeEvent.java @@ -0,0 +1,33 @@ +package source.tuner; + +public class TunerChangeEvent +{ + private Tuner mTuner; + private Event mEvent; + + public TunerChangeEvent( Tuner tuner, Event event ) + { + mTuner = tuner; + mEvent = event; + } + + public Tuner getTuner() + { + return mTuner; + } + + public Event getEvent() + { + return mEvent; + } + + public enum Event + { + ADD, + REMOVE, + CHANNEL_COUNT, + FREQUENCY, + SAMPLE_RATE, + SPECTRAL_DISPLAY; + } +} diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 93c22339..65a6d859 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -221,6 +221,11 @@ public abstract class TunerController implements Tunable return source; } + + public int getChannelCount() + { + return mTunedChannels.size(); + } /** * Indicates if the tuner's LO frequency must be updated in order to diff --git a/src/source/tuner/TunerType.java b/src/source/tuner/TunerType.java index ea58a9a7..75dc06ca 100644 --- a/src/source/tuner/TunerType.java +++ b/src/source/tuner/TunerType.java @@ -19,7 +19,7 @@ package source.tuner; public enum TunerType { - AIRSPY_R820T( "R820T" ), + AIRSPY_R820T( "R820T/R820T2" ), ELONICS_E4000( "E4000" ), ETTUS_WBX( "WBX" ), ETTUS_VARIOUS( "Ettus Tuner" ), diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 72fe4506..4637bfc8 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -29,6 +29,7 @@ import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; +import source.tuner.TunerChangeEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; @@ -137,8 +138,23 @@ public class AirspyTuner extends Tuner //Consider implementing a fractional resampler to get a correct 48 kHz //output sample rate - return mController.getChannel( this, channel ); + TunerChannelSource source = mController.getChannel( this, channel ); + + if( source != null ) + { + broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + } + + return source; } + + + + @Override + public int getChannelCount() + { + return mController.getChannelCount(); + } @Override public void releaseChannel( TunerChannelSource source ) diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java index 51cc2ba3..b14e0e77 100644 --- a/src/source/tuner/ettus/B100Tuner.java +++ b/src/source/tuner/ettus/B100Tuner.java @@ -105,6 +105,13 @@ public class B100Tuner extends Tuner //TODO: pass this call to the tuner controller return null; } + + @Override + public int getChannelCount() + { + // TODO Auto-generated method stub + return 0; + } @Override public void releaseChannel( TunerChannelSource source ) diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index 7c304c61..e2a1bcfa 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -28,6 +28,7 @@ import settings.SettingsManager; import source.SourceException; import source.tuner.MixerTuner; import source.tuner.MixerTunerDataLine; +import source.tuner.TunerChangeEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; @@ -113,9 +114,22 @@ public class FCDTuner extends MixerTuner public TunerChannelSource getChannel( TunerChannel tunerChannel ) throws RejectedExecutionException, SourceException { - return mController.getChannel( this, tunerChannel ); + TunerChannelSource source = mController.getChannel( this, tunerChannel ); + + if( source != null ) + { + broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + } + + return source; } + @Override + public int getChannelCount() + { + return mController.getChannelCount(); + } + /** * Releases the tuned channel so that the tuner controller can tune to * other frequencies as needed. diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java index 308e67c4..aee8570b 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -29,6 +29,7 @@ import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; +import source.tuner.TunerChangeEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; @@ -108,9 +109,22 @@ public class HackRFTuner extends Tuner public TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException { - return mController.getChannel( this, channel ); + TunerChannelSource source = mController.getChannel( this, channel ); + + if( source != null ) + { + broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + } + + return source; } + @Override + public int getChannelCount() + { + return mController.getChannelCount(); + } + @Override public void releaseChannel( TunerChannelSource source ) { diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 39b6f0f1..55c7dfed 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -30,6 +30,7 @@ import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; +import source.tuner.TunerChangeEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; @@ -157,6 +158,11 @@ public class RTL2832Tuner extends Tuner try { source = mController.getChannel( this, channel ); + + if( source != null ) + { + broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + } } catch( Exception e ) { @@ -165,6 +171,13 @@ public class RTL2832Tuner extends Tuner return source; } + + @Override + public int getChannelCount() + { + return mController.getChannelCount(); + } + /** * Releases the tuned channel so that the tuner controller can tune to * other frequencies as needed. From 218eeb1848bc9b6339aab23537eee348d3d0c920 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 12 Mar 2016 12:54:48 -0500 Subject: [PATCH 53/74] WIP #34 - more work on tuner model and view. Includes new tuner configuration and tuner configuration assignment editors. Beginning of tuner editor panel rework. Removes most of the remaining JTree configuration pieces. Updates the settings manager to load and save tuners and refactors all of the tuner configuration management out into the tuner configuration model. --- src/controller/BaseNode.java | 196 -------- src/controller/ConfigurableNode.java | 54 --- .../ConfigurationControllerModel.java | 246 ---------- src/controller/ConfigurationTreePanel.java | 117 ----- src/controller/ControllerPanel.java | 76 +-- .../ControllerTreeCellRenderer.java | 127 ----- src/controller/EmptyEditor.java | 59 --- src/controller/OldConfigurationEditor.java | 74 --- src/gui/SDRTrunk.java | 78 ++- src/gui/editor/EmptyEditor.java | 2 +- src/module/decode/p25/P25DecodeEditor.java | 2 +- src/settings/Settings.java | 24 +- src/settings/SettingsManager.java | 168 +++---- src/source/SourceManager.java | 18 +- src/source/TunerModel.java | 184 -------- .../recording/RecordingEditorPanel.java | 130 ----- src/source/recording/RecordingGroupNode.java | 101 ---- src/source/recording/RecordingNode.java | 82 ---- .../recording/RecordingSourceManager.java | 2 +- src/source/tuner/Tuner.java | 21 +- src/source/tuner/TunerController.java | 1 + src/source/tuner/TunerEditor.java | 207 +++++--- src/source/tuner/TunerEditorOld.java | 75 +++ ...{TunerChangeEvent.java => TunerEvent.java} | 7 +- src/source/tuner/TunerGroupNode.java | 35 -- src/source/tuner/TunerManager.java | 199 +------- src/source/tuner/TunerModel.java | 294 ++++++++++++ src/source/tuner/TunerNode.java | 166 ------- src/source/tuner/TunerSelectionListener.java | 23 - src/source/tuner/TunerType.java | 2 +- src/source/{ => tuner}/TunerViewPanel.java | 47 +- src/source/tuner/airspy/AirspyTuner.java | 12 +- .../airspy/AirspyTunerConfiguration.java | 2 +- .../airspy/AirspyTunerConfigurationPanel.java | 68 +-- .../tuner/airspy/AirspyTunerController.java | 2 +- .../TunerConfiguration.java | 4 +- .../TunerConfigurationAssignment.java | 4 +- .../TunerConfigurationEditor.java | 20 + .../TunerConfigurationEvent.java | 45 ++ .../TunerConfigurationFactory.java | 64 +++ .../TunerConfigurationModel.java | 370 +++++++++++++++ src/source/tuner/ettus/B100Tuner.java | 8 +- src/source/tuner/fcd/FCDTuner.java | 12 +- src/source/tuner/fcd/FCDTunerController.java | 2 +- .../fcd/proV1/FCD1TunerConfiguration.java | 2 +- .../proV1/FCD1TunerConfigurationPanel.java | 50 +- .../tuner/fcd/proV1/FCD1TunerController.java | 2 +- .../fcd/proplusV2/FCD2TunerConfiguration.java | 2 +- .../FCD2TunerConfigurationPanel.java | 90 ++-- .../fcd/proplusV2/FCD2TunerController.java | 2 +- src/source/tuner/hackrf/HackRFTuner.java | 12 +- .../hackrf/HackRFTunerConfiguration.java | 2 +- .../hackrf/HackRFTunerConfigurationPanel.java | 82 ++-- .../tuner/hackrf/HackRFTunerController.java | 2 +- src/source/tuner/rtl/RTL2832Tuner.java | 12 +- .../tuner/rtl/RTL2832TunerController.java | 2 - .../tuner/rtl/e4k/E4KTunerConfiguration.java | 2 +- .../rtl/e4k/E4KTunerConfigurationPanel.java | 94 ++-- .../tuner/rtl/e4k/E4KTunerController.java | 2 +- .../rtl/r820t/R820TTunerConfiguration.java | 2 +- ... => R820TTunerConfigurationEditorOld.java} | 135 +++--- .../tuner/rtl/r820t/R820TTunerController.java | 18 +- .../tuner/rtl/r820t/R820TTunerEditor.java | 446 ++++++++++++++++++ .../rtl/r820t/R820TTunerEditorPanel.java | 121 ----- src/spectrum/SpectralDisplayPanel.java | 55 ++- src/spectrum/SpectrumFrame.java | 12 +- 66 files changed, 1983 insertions(+), 2594 deletions(-) delete mode 100644 src/controller/BaseNode.java delete mode 100644 src/controller/ConfigurableNode.java delete mode 100644 src/controller/ConfigurationControllerModel.java delete mode 100644 src/controller/ConfigurationTreePanel.java delete mode 100644 src/controller/ControllerTreeCellRenderer.java delete mode 100644 src/controller/EmptyEditor.java delete mode 100644 src/controller/OldConfigurationEditor.java delete mode 100644 src/source/TunerModel.java delete mode 100644 src/source/recording/RecordingEditorPanel.java delete mode 100644 src/source/recording/RecordingGroupNode.java delete mode 100644 src/source/recording/RecordingNode.java create mode 100644 src/source/tuner/TunerEditorOld.java rename src/source/tuner/{TunerChangeEvent.java => TunerEvent.java} (68%) delete mode 100644 src/source/tuner/TunerGroupNode.java create mode 100644 src/source/tuner/TunerModel.java delete mode 100644 src/source/tuner/TunerNode.java delete mode 100644 src/source/tuner/TunerSelectionListener.java rename src/source/{ => tuner}/TunerViewPanel.java (61%) rename src/source/tuner/{ => configuration}/TunerConfiguration.java (96%) rename src/source/tuner/{ => configuration}/TunerConfigurationAssignment.java (96%) create mode 100644 src/source/tuner/configuration/TunerConfigurationEditor.java create mode 100644 src/source/tuner/configuration/TunerConfigurationEvent.java create mode 100644 src/source/tuner/configuration/TunerConfigurationFactory.java create mode 100644 src/source/tuner/configuration/TunerConfigurationModel.java rename src/source/tuner/rtl/r820t/{R820TTunerConfigurationPanel.java => R820TTunerConfigurationEditorOld.java} (88%) create mode 100644 src/source/tuner/rtl/r820t/R820TTunerEditor.java delete mode 100644 src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java diff --git a/src/controller/BaseNode.java b/src/controller/BaseNode.java deleted file mode 100644 index 89bbdfa6..00000000 --- a/src/controller/BaseNode.java +++ /dev/null @@ -1,196 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - -import java.awt.Color; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; - -public class BaseNode extends DefaultMutableTreeNode - implements Comparable -{ - private static final long serialVersionUID = 1L; - - /** - * Model backing the JTree and nodes. Since we're primarily interacting - * with the model via JTree tree nodes via context menus, injecting a - * reference to the model facilitates nodal access to the model's methods - */ - protected ConfigurationControllerModel mModel = null; - - /** - * Implements a base node that has a reference to the underlying - * tree data model to facilitate tree node operations from nodal context - * menus. - */ - public BaseNode( Object object ) - { - super( object ); - } - - /** - * Override this method to return a custom icon for the node - * @return - */ - public String getIconPath() - { - return null; - } - - public Color getBackgroundColor() - { - return null; - } - - public String toString() - { - if( getUserObject() != null ) - { - return (String)getUserObject(); - } - else - { - return "SDRTrunk"; - } - } - - public JPanel getEditor() - { - return new EmptyEditor(); - } - - /** - * Comparator to support nodal sorting/ordering - */ - @Override - public int compareTo( BaseNode node ) - { - return toString().compareTo( node.toString() ); - } - - /** - * This method only needs to be called once on the root node and all nodes - * in the tree will then have a reference to the model via getModel() - */ - public void setModel( ConfigurationControllerModel model ) - { - mModel = model; - } - - /** - * Returns reference to the SystemTreeModel. Only ConfigurableNode types - * or children are allowed in this tree, so this method assumes that the - * node or parent is of ConfigurableNode class or subclass. - * - * Reference to the model is required to enable node context operations - * like adding/deleting/stopping/starting. - */ - public ConfigurationControllerModel getModel() - { - if( mModel == null ) - { - TreeNode parent = getParent(); - - if( parent != null ) - { - return ((BaseNode)parent).getModel(); - } - else - { - return null; - } - } - else - { - return mModel; - } - } - - /** - * Supports a context menu for any children of the BaseNode. Override the - * method in the child node to provide a custom context menu for that node. - */ - public JPopupMenu getContextMenu() - { - return null; - } - - /** - * Invokes a recursive delete() on all children to perform any cleanup - * operations prior to deleting. Override this method to perform cleanup - * operations specific to the node. Remember to also either invoke - * super.delete(), or specifically call .delete() on any children of the - * node with the custom delete method. - */ - public void delete() - { - for( int x = 0; x < getChildCount(); x++ ) - { - BaseNode node = (BaseNode)getChildAt( x ); - - //Tell the node to delete its children - node.delete(); - - //Delete the child node - getModel().deleteNode( node ); - } - } - - public void show() - { - getModel().showNode( this ); - } - - public void refresh() - { - if( getModel() != null ) - { - getModel().nodeChanged( this ); - } - } - - public void sort() - { - List nodesToSort = new ArrayList(); - - while( children().hasMoreElements() ) - { - BaseNode child = (BaseNode)children().nextElement(); - - nodesToSort.add( child ); - - getModel().removeNodeFromParent( child ); - } - - if( !nodesToSort.isEmpty() ) - { - Collections.sort( nodesToSort ); - - for( BaseNode node: nodesToSort ) - { - getModel().insertNodeInto( node, this, getChildCount() ); - } - } - } -} diff --git a/src/controller/ConfigurableNode.java b/src/controller/ConfigurableNode.java deleted file mode 100644 index 77da7453..00000000 --- a/src/controller/ConfigurableNode.java +++ /dev/null @@ -1,54 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - -import playlist.PlaylistManager; - -public abstract class ConfigurableNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - private PlaylistManager mPlaylistManager; - - public ConfigurableNode( PlaylistManager playlistManager, Object object ) - { - super( object ); - - mPlaylistManager = playlistManager; - } - - protected PlaylistManager getPlaylistManager() - { - return mPlaylistManager; - } - - /** - * Saves changes to the node - */ - public void save() - { - mPlaylistManager.save(); - - BaseNode parent = (BaseNode)getParent(); - - if( parent instanceof ConfigurableNode ) - { - ((ConfigurableNode)parent).sort(); - } - } -} diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java deleted file mode 100644 index c3f28c87..00000000 --- a/src/controller/ConfigurationControllerModel.java +++ /dev/null @@ -1,246 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014,2015 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 - ******************************************************************************/ -package controller; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.swing.JTree; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreePath; - -import module.decode.DecoderType; -import settings.SettingsManager; -import source.SourceManager; -import source.tuner.Tuner; -import source.tuner.TunerGroupNode; -import source.tuner.TunerNode; -import source.tuner.TunerSelectionListener; -import controller.channel.Channel; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; - -public class ConfigurationControllerModel extends DefaultTreeModel -{ - private static final long serialVersionUID = 1L; - - private JTree mTree; - - private ArrayList mTunerSelectionListeners = - new ArrayList(); - - private TunerGroupNode mTunerGroupNode; - - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SettingsManager mSettingsManager; - private SourceManager mSourceManager; - - /** - * Implements a merged system controller and (tree) model. The user will - * primarily interact with the system nodes via the tree view, so from a - * design perspective, the model and the control are necessarily merged to - * work well with the JTree as the system view. - */ - public ConfigurationControllerModel( ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - SettingsManager settingsManager, - SourceManager sourceManager ) - { - super( new BaseNode( null ) ); - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSettingsManager = settingsManager; - mSourceManager = sourceManager; - - /** - * Give the root node a reference to this model, so that all nodes can - * invoke add/delete operations on the tree via context menu - */ - ((BaseNode)getRoot()).setModel( this ); - } - - public JTree getTree() - { - return mTree; - } - - /** - * init() gets invoked manually/externally, after all of the references - * are established in the constructor - */ - public void init() - { - /** - * Add the tuner group node - */ - mTunerGroupNode = new TunerGroupNode(); - insertNodeInto( mTunerGroupNode, (MutableTreeNode)root, 0 ); - addTuners( mTunerGroupNode ); - - /** - * Add the recording group node - */ -// mRecordingGroupNode = new RecordingGroupNode(); -// insertNodeInto( mRecordingGroupNode, (MutableTreeNode)root, 1 ); -// mRecordingGroupNode.loadRecordings(); - -// /** -// * Add the playlist node -// */ -// mPlaylistNode = new PlaylistNode( mChannelModel, mChannelProcessingManager, -// mPlaylistManager, mSettingsManager, mSourceManager ); -// insertNodeInto( mPlaylistNode, (MutableTreeNode)root, 1 ); -// mPlaylistNode.loadPlaylist(); - } - - public void showNode( DefaultMutableTreeNode node ) - { - if( mTree != null && node != null ) - { - mTree.setSelectionPath( new TreePath( node.getPath() ) ); - } - } - - public void setTree( JTree tree ) - { - mTree = tree; - } - - private void addTuners( TunerGroupNode parent ) - { - List tuners = mSourceManager.getTunerManager().getTuners(); - - for( Tuner tuner: tuners ) - { - TunerNode child = new TunerNode( mChannelModel, - mChannelProcessingManager, mSettingsManager, tuner ); - - insertNodeInto( child, parent, parent.getChildCount() ); - - parent.sort(); - - show( child ); - } - - } - - /** - * Convenience method to add a new default system, site and channel to - * the tree - */ - public void createChannel( long frequency, DecoderType decoder ) - { - throw new UnsupportedOperationException( "This has to be updated to new channel model format" ); -// System system = new System(); -// -// Site site = new Site(); -// system.addSite( site ); -// -// Channel channel = new Channel(); -// channel.setDecodeConfiguration( -// DecodeConfigFactory.getDecodeConfiguration( decoder ) ); -// SourceConfigTuner sourceConfig = new SourceConfigTuner(); -// sourceConfig.setFrequency( frequency ); -// channel.setSourceConfiguration( sourceConfig ); -// mResourceManager.getChannelManager().channelChanged( new ChannelEvent( channel, Event.NOTIFICATION_ADD ) ); -// channel.setEnabled( true, true ); -// site.addChannel( channel ); -// -// mPlaylistNode.getPlaylist().getSystemList().addSystem( system ); -// -// SystemNode systemNode = new SystemNode( system ); -// SystemListNode systemListNode = mPlaylistNode.getSystemListNode(); -// addNode( systemNode, systemListNode, systemListNode.getChildCount() ); -// systemNode.init(); -// -// systemNode.save(); - } - - public void addNode( BaseNode child, BaseNode parent, int index ) - { - insertNodeInto( child, parent, index ); - show( child ); - } - - public void deleteNode( BaseNode node ) - { - node.delete(); - } - - private void show( BaseNode node ) - { - if( mTree != null ) - { - mTree.scrollPathToVisible( new TreePath( node.getPath() ) ); - } - } - - public void addListener( TunerSelectionListener listener ) - { - mTunerSelectionListeners.add( listener ); - } - - public void removeListener( TunerSelectionListener listener ) - { - mTunerSelectionListeners.remove( listener ); - } - - /** - * Notifies registered listeners that the tuner has been selected - */ - public void fireTunerSelectedEvent( Tuner tuner ) - { - Iterator it = - mTunerSelectionListeners.iterator(); - - while( it.hasNext() ) - { - it.next().tunerSelected( tuner ); - } - } - - /** - * Fires a tuner selection event for the first tuner listed in the tree. - * This is mainly used on application startup to show the spectral display - * of the first tuner in the spectrum panel. - */ - public void fireFirstTunerSelected() - { - if( mTunerGroupNode.getChildCount() > 0 ) - { - TunerNode node = (TunerNode)mTunerGroupNode.getChildAt( 0 ); - - fireTunerSelectedEvent( node.getTuner() ); - } - } - - public ArrayList getCurrentChannelConfigs() - { - return null; - } - - private ArrayList getChannelConfigs( DefaultMutableTreeNode parent ) - { - return null; - } -} diff --git a/src/controller/ConfigurationTreePanel.java b/src/controller/ConfigurationTreePanel.java deleted file mode 100644 index 9644f1b8..00000000 --- a/src/controller/ConfigurationTreePanel.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; - -import javax.swing.JPanel; -import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.SwingUtilities; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.TreeSelectionModel; - -import net.miginfocom.swing.MigLayout; - -public class ConfigurationTreePanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - protected JTree mSystemTree; - protected ConfigurationControllerModel mTreeModel; - - public ConfigurationTreePanel( ConfigurationControllerModel model ) - { - mTreeModel = model; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill][]") ); - - mSystemTree = new JTree( mTreeModel ); - mSystemTree.getSelectionModel().setSelectionMode( - TreeSelectionModel.SINGLE_TREE_SELECTION ); - mSystemTree.setShowsRootHandles( true ); - mSystemTree.addMouseListener( new MouseHandler() ); - mSystemTree.setCellRenderer( new ControllerTreeCellRenderer() ); - - //Give the model a reference to the tree, so that it can force newly - //added nodes to be displayed - mTreeModel.setTree( mSystemTree ); - - JScrollPane treeScroll = new JScrollPane( mSystemTree ); - add( treeScroll, "cell 0 0,span,grow" ); - } - - public void addTreeSelectionListener( TreeSelectionListener listener ) - { - mSystemTree.addTreeSelectionListener( listener ); - } - - public class MouseHandler implements MouseListener - { - public MouseHandler() - { - } - - @Override - public void mouseClicked( MouseEvent event ) - { - if( SwingUtilities.isRightMouseButton( event ) ) - { - int row = mSystemTree.getRowForLocation( event.getX(), - event.getY() ); - - if( row != -1 ) - { - mSystemTree.setSelectionRow( row ); - - Object selectedNode = - mSystemTree.getLastSelectedPathComponent(); - - if( selectedNode instanceof BaseNode ) - { - JPopupMenu contextMenu = - ((BaseNode)selectedNode).getContextMenu(); - - if( contextMenu != null ) - { - contextMenu.show( mSystemTree, - event.getX(), - event.getY() ); - } - } - } - } - } - - @Override - public void mousePressed( MouseEvent e ){} - @Override - public void mouseReleased( MouseEvent e ){} - @Override - public void mouseEntered( MouseEvent e ){} - @Override - public void mouseExited( MouseEvent e ){} - } -} diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index bcb21c13..a90becf8 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -35,8 +35,8 @@ import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import source.SourceManager; -import source.TunerViewPanel; -import source.TunerModel; +import source.tuner.TunerModel; +import source.tuner.TunerViewPanel; import spectrum.ChannelSpectrumPanel; import alias.AliasController; import alias.AliasModel; @@ -55,36 +55,21 @@ public class ControllerPanel extends JPanel { private static final long serialVersionUID = 1L; - private ChannelList mChannelStateList; - - private TunerViewPanel mTunerManagerPanel; - - private CallEventPanel mCallEventPanel; - - private MessageActivityPanel mMessageActivityPanel; - - private ChannelSpectrumPanel mChannelSpectrumPanel; - private AliasController mAliasController; - - private ChannelController mChannelController; - - private JideTabbedPane mTabbedPane; - -// protected ConfigurationTreePanel mSystemControlViewPanel; -// protected OldConfigurationEditor mConfigurationEditor; -// protected JideSplitPane mSystemControlSplitPane; - - protected JTable mChannelActivityTable = new JTable(); private AudioPanel mAudioPanel; - private MapPanel mMapPanel; - + private CallEventPanel mCallEventPanel; + private ChannelController mChannelController; + private ChannelList mChannelStateList; private ChannelModel mChannelModel; - private ConfigurationControllerModel mController; - protected SettingsManager mSettingsManager; + private ChannelSpectrumPanel mChannelSpectrumPanel; + private MapPanel mMapPanel; + private MessageActivityPanel mMessageActivityPanel; + private TunerViewPanel mTunerManagerPanel; + + private JideTabbedPane mTabbedPane; + protected JTable mChannelActivityTable = new JTable(); public ControllerPanel( AudioManager audioManager, - ConfigurationControllerModel controller, AliasModel aliasModel, ChannelModel channelModel, ChannelMapModel channelMapModel, @@ -95,29 +80,27 @@ public class ControllerPanel extends JPanel TunerModel tunerModel ) { mChannelModel = channelModel; - mController = controller; - mSettingsManager = settingsManager; - mAudioPanel = new AudioPanel( mSettingsManager, sourceManager, audioManager ); + mAudioPanel = new AudioPanel( settingsManager, sourceManager, audioManager ); - mMapPanel = new MapPanel( mapService, mSettingsManager ); + mMapPanel = new MapPanel( mapService, settingsManager ); mMessageActivityPanel = new MessageActivityPanel( channelProcessingManager ); - mCallEventPanel = new CallEventPanel( mSettingsManager, channelProcessingManager ); + mCallEventPanel = new CallEventPanel( settingsManager, channelProcessingManager ); - mChannelSpectrumPanel = new ChannelSpectrumPanel( mSettingsManager, + mChannelSpectrumPanel = new ChannelSpectrumPanel( settingsManager, channelProcessingManager ); mChannelStateList = new ChannelList( channelModel, channelProcessingManager, - mSettingsManager ); + settingsManager ); mChannelController = new ChannelController( channelModel, channelMapModel, sourceManager, aliasModel ); - mAliasController = new AliasController( aliasModel, mSettingsManager ); + mAliasController = new AliasController( aliasModel, settingsManager ); - mTunerManagerPanel = new TunerViewPanel( tunerModel, mSettingsManager ); + mTunerManagerPanel = new TunerViewPanel( tunerModel ); init(); } @@ -128,17 +111,6 @@ public class ControllerPanel extends JPanel "[grow,fill]", "[grow,fill]") ); -// //System Configuration View and Editor -// mConfigurationEditor = new OldConfigurationEditor(); -// -// mSystemControlViewPanel = new ConfigurationTreePanel( mController ); -// mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor ); - -// mSystemControlSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); -// mSystemControlSplitPane.setDividerSize( 5 ); -// mSystemControlSplitPane.add( mSystemControlViewPanel ); -// mSystemControlSplitPane.add( mConfigurationEditor ); - //Tabbed View - configuration, calls, messages, map mTabbedPane = new JideTabbedPane(); mTabbedPane.setFont( this.getFont() ); @@ -146,7 +118,6 @@ public class ControllerPanel extends JPanel mTabbedPane.addTab( "Tuners", mTunerManagerPanel ); mTabbedPane.addTab( "Channels", mChannelController ); mTabbedPane.addTab( "Aliases", mAliasController ); -// mTabbedPane.addTab( "Configuration", mSystemControlSplitPane ); mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel ); mTabbedPane.addTab( "Events", mCallEventPanel ); mTabbedPane.addTab( "Messages", mMessageActivityPanel ); @@ -191,6 +162,10 @@ public class ControllerPanel extends JPanel audioChannelListSplit.setDividerSize( 5 ); audioChannelListSplit.add( mAudioPanel ); audioChannelListSplit.add( channelStateListScroll ); + + //Set preferred size to influence the split between these panels + audioChannelListSplit.setPreferredSize( new Dimension( 400, 500 ) ); + mTabbedPane.setPreferredSize( new Dimension( 880, 500 ) ); JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); channelSplit.setDividerSize( 5 ); @@ -199,9 +174,4 @@ public class ControllerPanel extends JPanel add( channelSplit ); } - - public ConfigurationControllerModel getController() - { - return mController; - } } diff --git a/src/controller/ControllerTreeCellRenderer.java b/src/controller/ControllerTreeCellRenderer.java deleted file mode 100644 index 42a791d4..00000000 --- a/src/controller/ControllerTreeCellRenderer.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Image; -import java.util.HashMap; - -import javax.swing.ImageIcon; -import javax.swing.JTree; -import javax.swing.tree.DefaultTreeCellRenderer; - -public class ControllerTreeCellRenderer extends DefaultTreeCellRenderer -{ - private static final long serialVersionUID = 1L; - - public static final int MAX_ICON_HEIGHT = 16; - private HashMap mIcons = new HashMap(); - - public ControllerTreeCellRenderer() - { - } - - @Override - public Component getTreeCellRendererComponent( JTree tree, - Object value, boolean selected, boolean expanded, boolean isLeaf, - int row, boolean focused ) - { - Component c = super.getTreeCellRendererComponent( tree, value, selected, - expanded, isLeaf, row, focused ); - - if( value instanceof BaseNode ) - { - BaseNode node = (BaseNode)value; - - ImageIcon icon = getIcon( node ); - - setIcon( icon ); - - Color background = node.getBackgroundColor(); - - if( background != null ) - { - setBackground( background ); - setOpaque( true ); - - if( selected ) - { - if( background == Color.CYAN ) - { - setForeground( Color.ORANGE ); - } - else if( background == Color.GREEN ) - { - setForeground( Color.MAGENTA ); - } - else if( background == Color.RED ) - { - setForeground( Color.YELLOW ); - } - } - } - else - { - setOpaque( false ); - } - } - - return c; - } - - private ImageIcon getIcon( BaseNode node ) - { - ImageIcon icon = null; - - String filepath = node.getIconPath(); - - if( filepath != null ) - { - if( mIcons.containsKey( filepath ) ) - { - icon = mIcons.get( filepath ); - } - else - { - icon = new ImageIcon( filepath ); - - if( icon.getIconHeight() > MAX_ICON_HEIGHT ) - { - Image image = icon.getImage(); - - double scale = (double)icon.getIconHeight() / (double)MAX_ICON_HEIGHT; - - int scaledWidth = (int)( (double)icon.getIconWidth() / scale ); - - Image scaledImage = image.getScaledInstance( scaledWidth, - MAX_ICON_HEIGHT, java.awt.Image.SCALE_SMOOTH ); - - mIcons.put( filepath, new ImageIcon( scaledImage ) ); - } - else - { - mIcons.put( filepath, icon ); - } - - } - } - - return icon; - } -} diff --git a/src/controller/EmptyEditor.java b/src/controller/EmptyEditor.java deleted file mode 100644 index 83f21de3..00000000 --- a/src/controller/EmptyEditor.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - -import java.awt.BorderLayout; -import java.awt.Dimension; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -public class EmptyEditor extends JPanel -{ - private static final long serialVersionUID = 1L; - - public EmptyEditor() - { - setLayout( new BorderLayout() ); - setPreferredSize( new Dimension( 200, (int)getPreferredSize().getHeight() ) ); - - String text = "" - + "

    SDR Trunk

    " - + "
    " - + "

    Use the tree structure to the left to configure SDRTrunk" - + " to use multiple SDR tuners and sound card sources to" - + " decode multiple trunked and digital radio channels.

    " - + "
    " - + "

    Each tree branch can be accessed by clicking on the branch" - + " and a configuration editor will appear here.

    " - + "
    " - + "

    Many of the branches contain context sensitive menus that " - + " you can access by right-clicking on the node.

    " - + "
    " - + "

    To begin, right-click on the 'Systems' branch under 'Playlist'" - + "and create a new 'System'. Right-click the 'System' node to create " - + "a new 'Site'. Right-click on the 'Site' node to create a new Channel.

    " - + "
    " - + "

    In the new channel, select a channel source and decoder " - + "and select event logging and recording options. Check 'Enabled'" - + " and hit the 'Save' button to begin decoding.

    " - + ""; - - add( new JLabel( text ), BorderLayout.NORTH ); - } -} diff --git a/src/controller/OldConfigurationEditor.java b/src/controller/OldConfigurationEditor.java deleted file mode 100644 index 29d504ee..00000000 --- a/src/controller/OldConfigurationEditor.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller; - - -import java.awt.BorderLayout; - -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; - -public class OldConfigurationEditor extends JPanel - implements TreeSelectionListener -{ - private static final long serialVersionUID = 1L; - - /** - * Host for configuration editor display panels - */ - public OldConfigurationEditor() - { - init(); - } - - public void init() - { - setPanel( new EmptyEditor() ); - } - - public void setPanel( JPanel display ) - { - removeAll(); - - setLayout( new BorderLayout() ); - - JScrollPane scroll = new JScrollPane( display ); - - add( scroll, BorderLayout.CENTER ); - - revalidate(); - repaint(); - } - - @Override - public void valueChanged( TreeSelectionEvent e ) - { - JTree tree = (JTree)e.getSource(); - - BaseNode node = - (BaseNode)tree.getLastSelectedPathComponent(); - - if( node != null ) - { - setPanel( node.getEditor() ); - } - } -} diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 16658a8f..dc84bb70 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -54,11 +54,12 @@ import org.slf4j.LoggerFactory; import playlist.PlaylistManager; import properties.SystemProperties; import record.RecorderManager; +import sample.Listener; import settings.SettingsManager; import source.SourceManager; -import source.TunerModel; -import source.tuner.Tuner; -import source.tuner.TunerSelectionListener; +import source.tuner.TunerEvent; +import source.tuner.TunerModel; +import source.tuner.configuration.TunerConfigurationModel; import spectrum.SpectralDisplayPanel; import util.TimeStamp; import alias.AliasModel; @@ -67,7 +68,6 @@ import audio.AudioManager; import com.jidesoft.swing.JideSplitPane; -import controller.ConfigurationControllerModel; import controller.ControllerPanel; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; @@ -77,7 +77,7 @@ import controller.channel.ChannelProcessingManager; import controller.channel.ChannelSelectionManager; import controller.channel.map.ChannelMapModel; -public class SDRTrunk +public class SDRTrunk implements Listener { private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); @@ -115,7 +115,10 @@ public class SDRTrunk //Log current properties setting SystemProperties.getInstance().logCurrentSettings(); - mSettingsManager = new SettingsManager(); + TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel(); + TunerModel tunerModel = new TunerModel( tunerConfigurationModel ); + + mSettingsManager = new SettingsManager( tunerConfigurationModel ); ThreadPoolManager threadPoolManager = new ThreadPoolManager(); @@ -129,12 +132,9 @@ public class SDRTrunk RecorderManager recorderManager = new RecorderManager( threadPoolManager ); - SourceManager sourceManager = new SourceManager( mSettingsManager, - threadPoolManager ); + SourceManager sourceManager = new SourceManager( tunerModel, + mSettingsManager, threadPoolManager ); - TunerModel tunerModel = new TunerModel(); - tunerModel.addTuners( sourceManager.getTunerManager().getTuners() ); - ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager( channelModel, channelMapModel, aliasModel, eventLogManager, recorderManager, @@ -156,18 +156,15 @@ public class SDRTrunk MapService mapService = new MapService( mSettingsManager ); channelProcessingManager.addMessageListener( mapService ); - ConfigurationControllerModel configurationControllerModel = - new ConfigurationControllerModel( channelModel, channelProcessingManager, - mSettingsManager, sourceManager ); - - mControllerPanel = new ControllerPanel( audioManager, configurationControllerModel, - aliasModel, channelModel, channelMapModel, channelProcessingManager, + mControllerPanel = new ControllerPanel( audioManager, aliasModel, + channelModel, channelMapModel, channelProcessingManager, mapService, mSettingsManager, sourceManager, tunerModel ); - mSpectralPanel = new SpectralDisplayPanel( configurationControllerModel, - channelModel, channelProcessingManager, mSettingsManager ); - - mTitle = getTitle(); + mSpectralPanel = new SpectralDisplayPanel( channelModel, + channelProcessingManager, mSettingsManager ); + + tunerModel.addListener( mSpectralPanel.getTunerEventListener() ); + tunerModel.addListener( this ); PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, aliasModel, channelModel, channelMapModel ); @@ -186,6 +183,8 @@ public class SDRTrunk //Initialize the GUI initGUI(); + + tunerModel.requestFirstTunerDisplay(); //Start the gui EventQueue.invokeLater( new Runnable() @@ -221,36 +220,17 @@ public class SDRTrunk "[grow,fill]", "[grow,fill]") ); - //init() the controller to load tuners and playlists - mControllerPanel.getController().init(); - /** * Setup main JFrame window */ + mTitle = getTitle(); mMainGui.setTitle( mTitle ); mMainGui.setBounds( 100, 100, 1280, 800 ); mMainGui.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); - - mControllerPanel.getController().addListener( new TunerSelectionListener() - { - @Override - public void tunerSelected( Tuner tuner ) - { - if( tuner != null ) - { - mMainGui.setTitle( mTitle + " - " + tuner.getName() ); - } - else - { - mMainGui.setTitle( mTitle ); - } - } - } ); - //Fire first tuner selection so it is displayed in the spectrum/waterfall - mControllerPanel.getController().fireFirstTunerSelected(); - - mSpectralPanel.setPreferredSize( new Dimension( 1280, 400 ) ); + //Set preferred sizes to influence the split + mSpectralPanel.setPreferredSize( new Dimension( 1280, 300 ) ); + mControllerPanel.setPreferredSize( new Dimension( 1280, 500 ) ); JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); splitPane.setDividerSize( 5 ); @@ -487,5 +467,13 @@ public class SDRTrunk return sb.toString(); } - + + @Override + public void receive( TunerEvent event ) + { + if( event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) + { + mMainGui.setTitle( mTitle + " - " + event.getTuner().getName() ); + } + } } diff --git a/src/gui/editor/EmptyEditor.java b/src/gui/editor/EmptyEditor.java index 4578df0b..128407fc 100644 --- a/src/gui/editor/EmptyEditor.java +++ b/src/gui/editor/EmptyEditor.java @@ -10,7 +10,7 @@ public class EmptyEditor extends Editor public EmptyEditor() { - setLayout( new MigLayout( "", "[center]", "[grow]" ) ); + setLayout( new MigLayout( "insets 0 0 0 0", "[center]", "[grow]" ) ); add( new JLabel( "Please select an item" ), "wrap" ); } diff --git a/src/module/decode/p25/P25DecodeEditor.java b/src/module/decode/p25/P25DecodeEditor.java index c87d1e7c..ccdfb281 100644 --- a/src/module/decode/p25/P25DecodeEditor.java +++ b/src/module/decode/p25/P25DecodeEditor.java @@ -33,7 +33,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceEditor; -import source.tuner.TunerEditor; +import source.tuner.TunerEditorOld; import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; diff --git a/src/settings/Settings.java b/src/settings/Settings.java index 464e4d46..1166592a 100644 --- a/src/settings/Settings.java +++ b/src/settings/Settings.java @@ -28,10 +28,10 @@ import map.DefaultIcon; import map.MapIcon; import settings.ColorSetting.ColorSettingName; import source.recording.RecordingConfiguration; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerConfiguration; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; import source.tuner.hackrf.HackRFTunerConfiguration; @@ -60,15 +60,15 @@ import source.tuner.rtl.r820t.R820TTunerConfiguration; @XmlRootElement( name = "SDRTrunk_settings" ) public class Settings { - private ArrayList mSettings = new ArrayList(); + private List mSettings = new ArrayList(); - private ArrayList mTunerConfiguration = + private List mTunerConfiguration = new ArrayList(); - private ArrayList mConfigurationAssignments = + private List mConfigurationAssignments = new ArrayList(); - private ArrayList mRecordingConfigurations = + private List mRecordingConfigurations = new ArrayList(); public Settings() @@ -76,7 +76,7 @@ public class Settings } @XmlElement( name="recording_configuration" ) - public ArrayList getRecordingConfigurations() + public List getRecordingConfigurations() { return mRecordingConfigurations; } @@ -98,12 +98,12 @@ public class Settings } @XmlElement( name="tuner_configuration" ) - public ArrayList getTunerConfigurations() + public List getTunerConfigurations() { return mTunerConfiguration; } - public void setTunerConfigurations( ArrayList configs ) + public void setTunerConfigurations( List configs ) { mTunerConfiguration = configs; } @@ -136,7 +136,7 @@ public class Settings } @XmlElement( name="setting" ) - public ArrayList getSettings() + public List getSettings() { return mSettings; } @@ -255,12 +255,12 @@ public class Settings } @XmlElement( name="tuner_configuration_assignment" ) - public ArrayList getSelectedTunerConfiguration() + public List getSelectedTunerConfiguration() { return mConfigurationAssignments; } - public void setSelectedTunerConfiguration( ArrayList configs ) + public void setSelectedTunerConfiguration( List configs ) { mConfigurationAssignments = configs; } diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index 3cac8abe..9529c9f6 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -30,6 +30,7 @@ import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; +import java.util.List; import javax.swing.ImageIcon; import javax.xml.bind.JAXBContext; @@ -45,19 +46,13 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import properties.SystemProperties; +import sample.Listener; import settings.ColorSetting.ColorSettingName; import source.recording.RecordingConfiguration; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; -import source.tuner.TunerType; -import source.tuner.airspy.AirspyTunerConfiguration; -import source.tuner.fcd.proV1.FCD1TunerConfiguration; -import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; -import source.tuner.hackrf.HackRFTunerConfiguration; -import source.tuner.rtl.e4k.E4KTunerConfiguration; -import source.tuner.rtl.r820t.R820TTunerConfiguration; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationModel; -public class SettingsManager +public class SettingsManager implements Listener { private final static Logger mLog = LoggerFactory.getLogger( SettingsManager.class ); @@ -76,8 +71,20 @@ public class SettingsManager private HashMap mResizedIcons = new HashMap(); - public SettingsManager() + private TunerConfigurationModel mTunerConfigurationModel; + private boolean mLoading = false; + + public SettingsManager( TunerConfigurationModel tunerConfigurationModel ) { + //TODO: move settings and icons into a SettingsModel and an IconModel + //and update this class to only provide loading, saving, and model + //change detection producing a save. + + mTunerConfigurationModel = tunerConfigurationModel; + + //Register for tuner config events so that we can save the settings + mTunerConfigurationModel.addListener( this ); + init(); } @@ -101,6 +108,15 @@ public class SettingsManager refreshIcons(); } + + @Override + public void receive( TunerConfigurationEvent t ) + { + if( !mLoading ) + { + save(); + } + } /** * Reloads the icon hash map, and sets up the default iconology. If the @@ -542,7 +558,7 @@ public class SettingsManager broadcastSettingChange( setting ); } - public ArrayList getRecordingConfigurations() + public List getRecordingConfigurations() { return mSettings.getRecordingConfigurations(); } @@ -559,98 +575,6 @@ public class SettingsManager save(); } - public ArrayList getTunerConfigurations( TunerType type ) - { - ArrayList configs = getSettings() - .getTunerConfigurations( type ); - - return configs; - } - - public void deleteTunerConfiguration( TunerConfiguration config ) - { - getSettings().removeTunerConfiguration( config ); - save(); - } - - public TunerConfiguration addNewTunerConfiguration( TunerType type, - String name ) - { - switch( type ) - { - case AIRSPY_R820T: - AirspyTunerConfiguration airspyConfig = - new AirspyTunerConfiguration( name ); - - getSettings().addTunerConfiguration( airspyConfig ); - - save(); - return airspyConfig; - case ELONICS_E4000: - E4KTunerConfiguration e4KConfig = - new E4KTunerConfiguration( name ); - - getSettings().addTunerConfiguration( e4KConfig ); - - save(); - return e4KConfig; - case FUNCUBE_DONGLE_PRO: - FCD1TunerConfiguration config = - new FCD1TunerConfiguration( name ); - - getSettings().addTunerConfiguration( config ); - - save(); - - return config; - case FUNCUBE_DONGLE_PRO_PLUS: - FCD2TunerConfiguration configPlus = - new FCD2TunerConfiguration( name ); - - getSettings().addTunerConfiguration( configPlus ); - - save(); - - return configPlus; - case HACKRF: - HackRFTunerConfiguration hackConfig = - new HackRFTunerConfiguration( name ); - - getSettings().addTunerConfiguration( hackConfig ); - - save(); - - return hackConfig; - case RAFAELMICRO_R820T: - R820TTunerConfiguration r820TConfig = - new R820TTunerConfiguration( name ); - - getSettings().addTunerConfiguration( r820TConfig ); - - save(); - - return r820TConfig; - default: - throw new IllegalArgumentException( "TunerConfiguration" - + "Directory - tuner type is unrecognized [" + - type.toString() + "]" ); - } - } - - public TunerConfigurationAssignment getSelectedTunerConfiguration( - TunerType type, String address ) - { - return mSettings.getConfigurationAssignment( type, address ); - } - - public void setSelectedTunerConfiguration( TunerType type, - String address, TunerConfiguration config ) - { - mSettings.setConfigurationAssignment( type, address, config.getName() ); - - save(); - } - public MapViewSetting getMapViewSetting( String name, GeoPosition position, int zoom ) { MapViewSetting loc = mSettings.getMapViewSetting( name ); @@ -681,6 +605,8 @@ public class SettingsManager public void save() { + saveTunerConfigurationModel(); + JAXBContext context = null; SystemProperties props = SystemProperties.getInstance(); @@ -759,6 +685,8 @@ public class SettingsManager */ public void load( Path settingsPath ) { + mLoading = true; + if( Files.exists( settingsPath ) ) { mLog.info( "SettingsManager - loading settings file [" + @@ -817,6 +745,36 @@ public class SettingsManager { mSettings = new Settings(); } + + loadTunerConfigurationModel(); + + mLoading = false; + } + + private void loadTunerConfigurationModel() + { + if( mSettings != null ) + { + mTunerConfigurationModel.clear(); + + mTunerConfigurationModel.addTunerConfigurations( + mSettings.getTunerConfigurations() ); + + mTunerConfigurationModel.addTunerConfigurationAssignments( + mSettings.getSelectedTunerConfiguration() ); + } + } + + private void saveTunerConfigurationModel() + { + if( mSettings != null ) + { + mSettings.setTunerConfigurations( + mTunerConfigurationModel.getTunerConfigurations() ); + + mSettings.setSelectedTunerConfiguration( + mTunerConfigurationModel.getTunerConfigurationAssignments() ); + } } public void broadcastSettingChange( Setting setting ) diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index c4a06e19..450a8b53 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -18,13 +18,15 @@ package source; import settings.SettingsManager; +import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.mixer.MixerEditor; import source.mixer.MixerManager; import source.recording.RecordingEditor; import source.recording.RecordingSourceManager; -import source.tuner.TunerEditor; +import source.tuner.TunerEditorOld; import source.tuner.TunerManager; +import source.tuner.TunerModel; import controller.ThreadPoolManager; public class SourceManager @@ -32,13 +34,19 @@ public class SourceManager private MixerManager mMixerManager; private RecordingSourceManager mRecordingSourceManager; private TunerManager mTunerManager; + private TunerModel mTunerModel; - public SourceManager( SettingsManager settingsManager, + public SourceManager( TunerModel tunerModel, + SettingsManager settingsManager, ThreadPoolManager threadPoolManager ) { + mTunerModel = tunerModel; mMixerManager = new MixerManager(); mRecordingSourceManager = new RecordingSourceManager( settingsManager ); - mTunerManager = new TunerManager( mMixerManager, settingsManager, threadPoolManager ); + mTunerManager = new TunerManager( mMixerManager, tunerModel, threadPoolManager ); + + //TODO: change mixer & recording managers to be models and hand them + //in via the constructor. Perform loading outside of this class. } public MixerManager getMixerManager() @@ -67,7 +75,7 @@ public class SourceManager retVal = mMixerManager.getSource( config ); break; case TUNER: - retVal = mTunerManager.getSource( config, bandwidth ); + retVal = mTunerModel.getSource( (SourceConfigTuner)config, bandwidth ); break; case RECORDING: retVal = mRecordingSourceManager.getSource( config, bandwidth ); @@ -89,7 +97,7 @@ public class SourceManager configuredPanel = new MixerEditor( this, config ); break; case TUNER: - configuredPanel = new TunerEditor( this, config ); + configuredPanel = new TunerEditorOld( this, config ); break; case RECORDING: configuredPanel = new RecordingEditor( this, config ); diff --git a/src/source/TunerModel.java b/src/source/TunerModel.java deleted file mode 100644 index 4e5655a4..00000000 --- a/src/source/TunerModel.java +++ /dev/null @@ -1,184 +0,0 @@ -package source; - -import java.text.DecimalFormat; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.table.AbstractTableModel; - -import sample.Listener; -import source.tuner.Tuner; -import source.tuner.TunerChangeEvent; - -public class TunerModel extends AbstractTableModel - implements Listener -{ - private static final long serialVersionUID = 1L; - - private static final int TUNER_CLASS = 0; - private static final int TUNER_TYPE = 1; - private static final int SAMPLE_RATE = 2; - private static final int FREQUENCY = 3; - private static final int CHANNEL_COUNT = 4; - private static final int SPECTRAL_DISPLAY = 5; - - private static final String MHZ = " MHz"; - private static final String[] COLUMNS = - { "Class", "Type", "Sample Rate", "Frequency", "Channels", "Spectrum" }; - - private List mTuners = new ArrayList<>(); - private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.00000" ); - private DecimalFormat mSampleRateFormat = new DecimalFormat( "0.000" ); - - public TunerModel() - { - } - - /** - * List of Tuners currently in the model - */ - public List getTuners() - { - return mTuners; - } - - public Tuner getTuner( int index ) - { - if( index < mTuners.size() ) - { - return mTuners.get( index ); - } - - return null; - } - - public void addTuners( List Tuners ) - { - for( Tuner tuner: Tuners ) - { - addTuner( tuner ); - } - } - - /** - * Adds the Tuner to this model - */ - public void addTuner( Tuner tuner ) - { - if( !mTuners.contains( tuner ) ) - { - mTuners.add( tuner ); - - int index = mTuners.indexOf( tuner ); - - fireTableRowsInserted( index, index ); - - tuner.addTunerChangeListener( this ); - } - } - - /** - * Removes the Tuner from this model - */ - public void removeTuner( Tuner tuner ) - { - if( mTuners.contains( tuner ) ) - { - tuner.removeTunerChangeListener( this ); - - int index = mTuners.indexOf( tuner ); - - mTuners.remove( tuner ); - - fireTableRowsDeleted( index, index ); - } - } - - - @Override - public void receive( TunerChangeEvent event ) - { - if( event.getTuner() != null ) - { - int index = mTuners.indexOf( event.getTuner() ); - - if( index >= 0 ) - { - switch( event.getEvent() ) - { - case CHANNEL_COUNT: - fireTableCellUpdated( index, CHANNEL_COUNT ); - break; - case FREQUENCY: - fireTableCellUpdated( index, FREQUENCY ); - break; - case SAMPLE_RATE: - fireTableCellUpdated( index, SAMPLE_RATE ); - break; - case SPECTRAL_DISPLAY: - fireTableCellUpdated( index, SPECTRAL_DISPLAY ); - default: - break; - } - } - } - } - - @Override - public int getRowCount() - { - return mTuners.size(); - } - - @Override - public int getColumnCount() - { - return 6; - } - - @Override - public Object getValueAt( int rowIndex, int columnIndex ) - { - if( rowIndex < mTuners.size() ) - { - Tuner tuner = mTuners.get( rowIndex ); - - switch( columnIndex ) - { - case TUNER_CLASS: - return tuner.getTunerClass().getVendorDescription(); - case TUNER_TYPE: - return tuner.getTunerType().getLabel(); - case SAMPLE_RATE: - int sampleRate = tuner.getSampleRate(); - - return mSampleRateFormat.format( sampleRate / 1E6D ) + MHZ; - case FREQUENCY: - try - { - long frequency = tuner.getFrequency(); - - return mFrequencyFormat.format( frequency / 1E6D ) + MHZ; - } - catch( Exception e ) - { - return 0; - } - case CHANNEL_COUNT: - return tuner.getChannelCount(); - case SPECTRAL_DISPLAY: - return "TBD"; - default: - break; - } - } - - return null; - } - - @Override - public String getColumnName( int columnIndex ) - { - return COLUMNS[ columnIndex ]; - } -} diff --git a/src/source/recording/RecordingEditorPanel.java b/src/source/recording/RecordingEditorPanel.java deleted file mode 100644 index 408538d5..00000000 --- a/src/source/recording/RecordingEditorPanel.java +++ /dev/null @@ -1,130 +0,0 @@ -package source.recording; - -import gui.control.JFrequencyControl; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.io.File; - -import javax.swing.JButton; -import javax.swing.JFileChooser; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JTextField; - -import net.miginfocom.swing.MigLayout; - -public class RecordingEditorPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - private RecordingNode mRecordingNode; - - private JTextField mRecordingAlias; - private JLabel mFilePathLabel; - - public RecordingEditorPanel( RecordingNode recordingNode ) - { - mRecordingNode = recordingNode; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][][grow]" ) ); - - add( new JLabel( "I/Q Recording" ), "span,align left" ); - - add( new JLabel( "Alias:" ) ); - - mRecordingAlias = new JTextField( mRecordingNode.getRecording() - .getRecordingConfiguration().getAlias() ); - - mRecordingAlias.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent arg0 ) - { - String alias = mRecordingAlias.getText(); - - if( alias != null ) - { - mRecordingNode.getRecording().setAlias( alias ); - } - } - - @Override - public void focusGained( FocusEvent arg0 ) {} - } ); - - add( mRecordingAlias, "wrap" ); - - JButton mFileSelectionButton = new JButton( "File ..." ); - mFileSelectionButton.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - if( mRecordingNode.getRecording().hasChannels() ) - { - JOptionPane.showMessageDialog( RecordingEditorPanel.this, - "This recording is currently in replay. Please " - + "disable any channels that are using this " - + "recording before changing the recording file." ); - } - else - { - final JFileChooser fc = new JFileChooser(); - - int returnVal = fc.showOpenDialog( RecordingEditorPanel.this ); - - if ( returnVal == JFileChooser.APPROVE_OPTION ) - { - File file = fc.getSelectedFile(); - - if( file != null && file.exists() ) - { - mRecordingNode.getRecording() - .getRecordingConfiguration() - .setFilePath( file.getAbsolutePath() ); - - if( mFilePathLabel != null ) - { - mFilePathLabel.setText( file.getName() ); - } - } - } - } - } - } ); - add( mFileSelectionButton ); - - String filePath = mRecordingNode.getRecording() - .getRecordingConfiguration().getFilePath(); - - if( filePath != null ) - { - mFilePathLabel = new JLabel( filePath ); - } - else - { - mFilePathLabel = new JLabel( "Please select a recording" ); - } - - add( mFilePathLabel, "wrap" ); - - add( new JLabel( "Center Frequency:" ), "span,align left" ); - - JFrequencyControl frequencyControl = new JFrequencyControl( - mRecordingNode.getRecording().getRecordingConfiguration() - .getCenterFrequency() ); - - frequencyControl.addListener( mRecordingNode.getRecording() ); - - add( frequencyControl, "span,align left" ); - } -} diff --git a/src/source/recording/RecordingGroupNode.java b/src/source/recording/RecordingGroupNode.java deleted file mode 100644 index 3e9d4631..00000000 --- a/src/source/recording/RecordingGroupNode.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.recording; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; - -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; - -import settings.SettingsManager; -import source.SourceManager; -import controller.BaseNode; - -public class RecordingGroupNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - private SettingsManager mSettingsManager; - - public RecordingGroupNode( SourceManager sourceManager, SettingsManager settingsManager ) - { - super( sourceManager ); - - mSettingsManager = settingsManager; - } - - private SourceManager getSourceManager() - { - return (SourceManager)getUserObject(); - } - - public String toString() - { - return "Recordings [" + this.getChildCount() + "]"; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu menu = new JPopupMenu(); - - JMenuItem addRecordingItem = new JMenuItem( "Add Recording" ); - - addRecordingItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - RecordingConfiguration config = new RecordingConfiguration(); - - mSettingsManager.addRecordingConfiguration( config ); - - Recording recording = getSourceManager() - .getRecordingSourceManager().addRecording( config ); - - RecordingNode node = new RecordingNode( getSourceManager(), recording ); - - getModel().addNode( node, RecordingGroupNode.this, 0 ); - - sort(); - - node.show(); - } - } ); - - menu.add( addRecordingItem ); - - return menu; - } - - public void loadRecordings() - { - List recordings = getSourceManager() - .getRecordingSourceManager().getRecordings(); - - for( Recording recording: recordings ) - { - RecordingNode node = new RecordingNode( getSourceManager(), recording ); - - getModel().addNode( node, this, 0 ); - } - - sort(); - } -} diff --git a/src/source/recording/RecordingNode.java b/src/source/recording/RecordingNode.java deleted file mode 100644 index 920602e3..00000000 --- a/src/source/recording/RecordingNode.java +++ /dev/null @@ -1,82 +0,0 @@ -package source.recording; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JMenuItem; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import source.SourceManager; -import controller.BaseNode; - -public class RecordingNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - private JPanel mEditor; - - private SourceManager mSourceManager; - - public RecordingNode( SourceManager sourceManager, Recording recording ) - { - super( recording ); - } - - public Recording getRecording() - { - return (Recording)getUserObject(); - } - - public String toString() - { - return getRecording().toString(); - } - - @Override - public JPanel getEditor() - { - if( mEditor == null ) - { - mEditor = new RecordingEditorPanel( this ); - } - - return mEditor; - } - - public JPopupMenu getContextMenu() - { - JPopupMenu menu = new JPopupMenu(); - - JMenuItem deleteItem = new JMenuItem( "Delete" ); - - deleteItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - if( getRecording().hasChannels() ) - { - JOptionPane.showMessageDialog( getModel().getTree(), - "Can't delete recording - currently in use. Please" - + " disable any channel(s) that are using this " - + "recording before deleting it", - "Can't delete recording", - JOptionPane.ERROR_MESSAGE ); - } - else - { - mSourceManager.getRecordingSourceManager() - .removeRecording( getRecording() ); - - getModel().removeNodeFromParent( RecordingNode.this ); - } - } - } ); - - menu.add( deleteItem ); - - return menu; - } -} diff --git a/src/source/recording/RecordingSourceManager.java b/src/source/recording/RecordingSourceManager.java index a047b170..fa0ae8b7 100644 --- a/src/source/recording/RecordingSourceManager.java +++ b/src/source/recording/RecordingSourceManager.java @@ -131,7 +131,7 @@ public class RecordingSourceManager private void loadRecordings() { - ArrayList recordingConfigurations = + List recordingConfigurations = mSettingsManager.getRecordingConfigurations(); mLog.info( "RecordingSourceManager - discovered [" + diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index cc4a8195..0a0215f2 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -31,6 +31,7 @@ import sample.Listener; import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; +import source.tuner.configuration.TunerConfiguration; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; @@ -55,7 +56,7 @@ public abstract class Tuner implements IFrequencyChangeProcessor, protected List mFrequencyChangeProcessors = new CopyOnWriteArrayList<>(); - protected List> mTunerChangeListeners = + protected List> mTunerChangeListeners = new ArrayList<>(); public Tuner( String name ) @@ -79,12 +80,6 @@ public abstract class Tuner implements IFrequencyChangeProcessor, mName = name; } - /** - * Return an editor panel for the tuner - */ - public abstract JPanel getEditor( SettingsManager settingsManager ); - - /** * Applies the settings from a saved tuner configuration setting object */ @@ -223,7 +218,7 @@ public abstract class Tuner implements IFrequencyChangeProcessor, /** * Registers the listener */ - public void addTunerChangeListener( Listener listener ) + public void addTunerChangeListener( Listener listener ) { mTunerChangeListeners.add( listener ); } @@ -231,7 +226,7 @@ public abstract class Tuner implements IFrequencyChangeProcessor, /** * Removes the registered listener */ - public void removeTunerChangeListener( Listener listener ) + public void removeTunerChangeListener( Listener listener ) { mTunerChangeListeners.remove( listener ); } @@ -239,9 +234,9 @@ public abstract class Tuner implements IFrequencyChangeProcessor, /** * Broadcasts the tuner change event */ - public void broadcast( TunerChangeEvent event ) + public void broadcast( TunerEvent event ) { - for( Listener listener: mTunerChangeListeners ) + for( Listener listener: mTunerChangeListeners ) { listener.receive( event ); } @@ -277,11 +272,11 @@ public abstract class Tuner implements IFrequencyChangeProcessor, if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) { - broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.FREQUENCY ) ); + broadcast( new TunerEvent( this, TunerEvent.Event.FREQUENCY ) ); } else if( event.getEvent() == Event.NOTIFICATION_SAMPLE_RATE_CHANGE ) { - broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.SAMPLE_RATE ) ); + broadcast( new TunerEvent( this, TunerEvent.Event.SAMPLE_RATE ) ); } } diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 65a6d859..832b4b2f 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -25,6 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.SourceException; +import source.tuner.configuration.TunerConfiguration; import source.tuner.frequency.FrequencyController; import source.tuner.frequency.FrequencyController.Tunable; import source.tuner.frequency.IFrequencyChangeProcessor; diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 2be792a1..18799074 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -1,75 +1,168 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ package source.tuner; -import controller.channel.Channel; -import controller.channel.ConfigurationValidationException; import gui.control.JFrequencyControl; -import source.SourceEditor; -import source.SourceManager; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; +import gui.editor.Editor; +import gui.editor.EmptyEditor; -public class TunerEditor extends SourceEditor +import java.awt.Dimension; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.RowFilter; +import javax.swing.table.TableRowSorter; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationModel; + +import com.jidesoft.swing.JideSplitPane; + +public class TunerEditor extends Editor { - private static final long serialVersionUID = 1L; - private JFrequencyControl mFrequencyControl; - - public TunerEditor( SourceManager sourceManager, - SourceConfiguration config ) - { - super( sourceManager, config ); - - initGUI(); - } + private static final long serialVersionUID = 1L; - public void reset() + private final static Logger mLog = LoggerFactory.getLogger( TunerEditor.class ); + + private TunerConfigurationModel mTunerConfigurationModel; + private JTable mTunerConfigurationTable; + private TableRowSorter mRowSorter; + private ConfigurationRowFilter mRowFilter = new ConfigurationRowFilter(); + private JFrequencyControl mFrequencyControl = new JFrequencyControl(); + + public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) { - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); + mTunerConfigurationModel = tunerConfigurationModel; + init(); } + public void init() + { + setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]" ) ); + + JPanel listPanel = new JPanel(); + listPanel.setLayout( new MigLayout( "fill,wrap 1", + "[grow,fill]", "[][][grow,fill][]" ) ); + listPanel.add( mFrequencyControl ); + +// mFrequencyControl.addListener( this ); +// /* Add frequency control as frequency change listener. */ +// mController.addListener( mFrequencyControl ); +// mFrequencyControl.setFrequency( mController.getFrequency(), false ); + + mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); + mRowSorter = new TableRowSorter<>( mTunerConfigurationModel ); + mRowSorter.setRowFilter( mRowFilter ); + mTunerConfigurationTable.setRowSorter( mRowSorter ); + + listPanel.add( new JLabel( "Tuner Configurations" ) ); + listPanel.add( mTunerConfigurationTable ); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout( new MigLayout( "insets 0 0 0 0", + "[grow,fill][grow,fill]", "[grow,fill]" ) ); + + JButton newButton = new JButton( "New" ); + buttonPanel.add( newButton ); + + JButton deleteButton = new JButton( "Delete" ); + buttonPanel.add( deleteButton ); + + listPanel.add( buttonPanel ); + + //Set preferred size on both scrollers to same width, so they split the space + JScrollPane listScroller = new JScrollPane( listPanel ); + listScroller.setPreferredSize( new Dimension( 200, 80 ) ); + + JPanel editorPanel = new EmptyEditor(); + JScrollPane editorScroller = new JScrollPane( editorPanel ); + editorScroller.setPreferredSize( new Dimension( 200, 80 ) ); + + JideSplitPane splitPane = + new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + splitPane.add( listScroller ); + splitPane.add( editorScroller ); + + add( splitPane ); + } + + @Override public void save() { - ((SourceConfigTuner)mConfig).setFrequency( mFrequencyControl.getFrequency() ); + } + + @Override + public void setItem( Tuner tuner ) + { + super.setItem( tuner ); + + if( hasItem() ) + { + mRowFilter.setTunerType( tuner.getTunerType() ); + + TunerConfiguration assigned = mTunerConfigurationModel + .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); + + int modelIndex = mTunerConfigurationModel.getRowIndex( assigned ); + + if( modelIndex >= 0 ) + { + int viewIndex = mTunerConfigurationTable.convertRowIndexToView( modelIndex ); + + if( viewIndex >= 0 ) + { + mTunerConfigurationTable.setRowSelectionInterval( viewIndex, viewIndex ); + } + } + } + else + { + mRowFilter.setTunerType( null ); + } + + mRowSorter.setRowFilter( mRowFilter ); } - private void initGUI() + public class ConfigurationRowFilter extends RowFilter { - mFrequencyControl = new JFrequencyControl(); + private TunerType mTunerType; - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); - - add( mFrequencyControl ); - } - - @Override - public void setConfiguration( Channel channel ) - { - // TODO Auto-generated method stub - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub + public ConfigurationRowFilter() + { + } + public void setTunerType( TunerType type ) + { + mTunerType = type; + } + + @Override + public boolean include( javax.swing.RowFilter.Entry entry ) + { + if( mTunerType == null ) + { + return true; + } + else + { + TunerConfigurationModel model = entry.getModel(); + TunerConfiguration config = model.getTunerConfiguration( entry.getIdentifier() ); + + if( config != null ) + { + return config.getTunerType() == mTunerType; + } + } + + return false; + } } + } diff --git a/src/source/tuner/TunerEditorOld.java b/src/source/tuner/TunerEditorOld.java new file mode 100644 index 00000000..b17339e0 --- /dev/null +++ b/src/source/tuner/TunerEditorOld.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014 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 + ******************************************************************************/ +package source.tuner; + +import controller.channel.Channel; +import controller.channel.ConfigurationValidationException; +import gui.control.JFrequencyControl; +import source.SourceEditor; +import source.SourceManager; +import source.config.SourceConfigTuner; +import source.config.SourceConfiguration; + +public class TunerEditorOld extends SourceEditor +{ + private static final long serialVersionUID = 1L; + private JFrequencyControl mFrequencyControl; + + public TunerEditorOld( SourceManager sourceManager, + SourceConfiguration config ) + { + super( sourceManager, config ); + + initGUI(); + } + + public void reset() + { + mFrequencyControl.setFrequency( + ((SourceConfigTuner)mConfig).getFrequency(), false ); + } + + public void save() + { + ((SourceConfigTuner)mConfig).setFrequency( mFrequencyControl.getFrequency() ); + } + + private void initGUI() + { + mFrequencyControl = new JFrequencyControl(); + + mFrequencyControl.setFrequency( + ((SourceConfigTuner)mConfig).getFrequency(), false ); + + add( mFrequencyControl ); + } + + @Override + public void setConfiguration( Channel channel ) + { + // TODO Auto-generated method stub + + } + + @Override + public void validateConfiguration() throws ConfigurationValidationException + { + // TODO Auto-generated method stub + + } +} diff --git a/src/source/tuner/TunerChangeEvent.java b/src/source/tuner/TunerEvent.java similarity index 68% rename from src/source/tuner/TunerChangeEvent.java rename to src/source/tuner/TunerEvent.java index 72bf366c..cccdc8fa 100644 --- a/src/source/tuner/TunerChangeEvent.java +++ b/src/source/tuner/TunerEvent.java @@ -1,11 +1,11 @@ package source.tuner; -public class TunerChangeEvent +public class TunerEvent { private Tuner mTuner; private Event mEvent; - public TunerChangeEvent( Tuner tuner, Event event ) + public TunerEvent( Tuner tuner, Event event ) { mTuner = tuner; mEvent = event; @@ -28,6 +28,7 @@ public class TunerChangeEvent CHANNEL_COUNT, FREQUENCY, SAMPLE_RATE, - SPECTRAL_DISPLAY; + REQUEST_MAIN_SPECTRAL_DISPLAY, + REQUEST_SECONDARY_SPECTRAL_DISPLAY; } } diff --git a/src/source/tuner/TunerGroupNode.java b/src/source/tuner/TunerGroupNode.java deleted file mode 100644 index 4a3c8247..00000000 --- a/src/source/tuner/TunerGroupNode.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner; - -import controller.BaseNode; - -public class TunerGroupNode extends BaseNode -{ - private static final long serialVersionUID = 1L; - - public TunerGroupNode() - { - super( null ); - } - - public String toString() - { - return "Tuners [" + this.getChildCount() + "]"; - } -} diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index 38b10e0a..d423d860 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014,2015 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -17,15 +17,7 @@ ******************************************************************************/ package source.tuner; -import gui.editor.EmptyEditor; - -import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.concurrent.RejectedExecutionException; - -import javax.swing.JPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,15 +26,10 @@ import org.usb4java.DeviceDescriptor; import org.usb4java.DeviceList; import org.usb4java.LibUsb; -import settings.SettingsManager; -import source.Source; import source.SourceException; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; import source.mixer.MixerManager; import source.tuner.airspy.AirspyTuner; import source.tuner.airspy.AirspyTunerController; -import source.tuner.airspy.AirspyTunerEditorPanel; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerController; import source.tuner.fcd.proplusV2.FCD2TunerController; @@ -51,7 +38,6 @@ import source.tuner.hackrf.HackRFTunerController; import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; -import source.tuner.rtl.e4k.E4KTunerEditorPanel; import source.tuner.rtl.r820t.R820TTunerController; import controller.ThreadPoolManager; @@ -60,17 +46,15 @@ public class TunerManager private final static Logger mLog = LoggerFactory.getLogger( TunerManager.class ); - private ArrayList mTuners = new ArrayList(); - private MixerManager mMixerManager; - private SettingsManager mSettingsManager; + private TunerModel mTunerModel; private ThreadPoolManager mThreadPoolManager; public TunerManager( MixerManager mixerManager, - SettingsManager settingsManager, + TunerModel tunerModel, ThreadPoolManager threadPoolManager ) { - mSettingsManager = settingsManager; + mTunerModel = tunerModel; mThreadPoolManager = threadPoolManager; initTuners(); @@ -84,57 +68,6 @@ public class TunerManager LibUsb.exit( null ); } - /** - * Iterates current tuners to get a tuner channel source for the frequency - * specified in the channel config's source config object - */ - public Source getSource( SourceConfiguration config, int bandwidth ) - { - TunerChannelSource retVal = null; - - if( config instanceof SourceConfigTuner ) - { - SourceConfigTuner tunerConfig = (SourceConfigTuner)config; - - TunerChannel tunerChannel = tunerConfig.getTunerChannel(); - - tunerChannel.setBandwidth( bandwidth ); - - Iterator it = mTuners.iterator(); - - Tuner tuner; - - while( it.hasNext() && retVal == null ) - { - tuner = it.next(); - - try - { - retVal = tuner.getChannel( tunerChannel ); - } - catch ( RejectedExecutionException ree ) - { - mLog.error( "couldn't provide tuner channel source", ree ); - } - catch ( SourceException e ) - { - mLog.error( "error obtaining channel from tuner [" + - tuner.getName() + "]", e ); - } - } - } - - return retVal; - } - - /** - * Get list of current tuners - */ - public List getTuners() - { - return mTuners; - } - /** * Loads all USB tuners and USB/Mixer tuner devices */ @@ -192,9 +125,19 @@ public class TunerManager if( status.isLoaded() ) { Tuner tuner = status.getTuner(); - sb.append( "] LOADED: " ); - sb.append( tuner.toString() ); - mTuners.add( tuner ); + + try + { + mTunerModel.addTuner( tuner ); + sb.append( "] LOADED: " ); + sb.append( tuner.toString() ); + } + catch( Exception e ) + { + sb.append( "] NOT LOADED: " ); + sb.append( status.getInfo() ); + sb.append( " Error:" + e.getMessage() ); + } } else { @@ -276,13 +219,6 @@ public class TunerManager AirspyTuner tuner = new AirspyTuner( airspyController ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch( SourceException se ) @@ -301,11 +237,6 @@ public class TunerManager { return new TunerInitStatus( null, "Ettus B100 tuner not currently " + "supported" ); -// case ETTUS_USRP_B100: -// mTuners.add( new B100Tuner( device ) ); -// status = sLOADED; -// reason = null; -// break; } private TunerInitStatus initFuncubeProTuner( Device device, @@ -328,13 +259,6 @@ public class TunerManager FCDTuner tuner = new FCDTuner( dataline, controller ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch ( SourceException e ) @@ -373,13 +297,6 @@ public class TunerManager FCDTuner tuner = new FCDTuner( dataline, controller ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch ( SourceException e ) @@ -412,13 +329,6 @@ public class TunerManager HackRFTuner tuner = new HackRFTuner( hackRFController ); - TunerConfiguration config = getTunerConfiguration( tuner ); - - if( config != null ) - { - tuner.apply( config ); - } - return new TunerInitStatus( tuner, "LOADED" ); } catch( SourceException se ) @@ -465,14 +375,6 @@ public class TunerManager RTL2832Tuner rtlTuner = new RTL2832Tuner( tunerClass, controller ); - TunerConfiguration config = - getTunerConfiguration( rtlTuner ); - - if( config != null ) - { - rtlTuner.apply( config ); - } - return new TunerInitStatus( rtlTuner, "LOADED" ); } catch( SourceException se ) @@ -492,14 +394,6 @@ public class TunerManager RTL2832Tuner rtlTuner = new RTL2832Tuner( tunerClass, controller ); - TunerConfiguration config = - getTunerConfiguration( rtlTuner ); - - if( config != null ) - { - rtlTuner.apply( config ); - } - return new TunerInitStatus( rtlTuner, "LOADED" ); } catch( SourceException se ) @@ -522,54 +416,6 @@ public class TunerManager return new TunerInitStatus( null, reason ); } - - /** - * Get a stored tuner configuration or create a default tuner configuration - * for the tuner type, connected at the address. - * - * Note: a named tuner configuration will be stored for for each tuner type - * and address combination. - * @param type - tuner type - * @param address - current usb address/port - * @return - stored tuner configuration or default configuration - */ - private TunerConfiguration getTunerConfiguration( Tuner tuner ) - { - TunerConfigurationAssignment selected = mSettingsManager - .getSelectedTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); - - ArrayList configs = mSettingsManager - .getTunerConfigurations( tuner.getTunerType() ); - - TunerConfiguration config = null; - - if( selected != null ) - { - for( TunerConfiguration tunerConfig: configs ) - { - if( tunerConfig.getName().contentEquals( selected.getTunerConfigurationName() ) ) - { - config = tunerConfig; - } - } - } - - //If we're still null at this point, create a default config - if( config == null ) - { - String name = "Default-" + tuner.getUniqueID(); - - config = mSettingsManager.addNewTunerConfiguration( - tuner.getTunerType(), name ); - } - - //Persist the config as the selected tuner configuration. The method - //auto-saves the setting - mSettingsManager.setSelectedTunerConfiguration( - tuner.getTunerType(), tuner.getUniqueID(), config ); - - return config; - } /** * Gets the first tuner mixer dataline that corresponds to the tuner class. @@ -625,15 +471,4 @@ public class TunerManager } } - public static JPanel getEditor( SettingsManager settingsManager, Tuner tuner ) - { - JPanel editor = tuner.getEditor( settingsManager ); - - if( editor != null ) - { - return editor; - } - - return new EmptyEditor(); - } } diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java new file mode 100644 index 00000000..14ca0a3d --- /dev/null +++ b/src/source/tuner/TunerModel.java @@ -0,0 +1,294 @@ +package source.tuner; + +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.RejectedExecutionException; + +import javax.swing.table.AbstractTableModel; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.Source; +import source.SourceException; +import source.config.SourceConfigTuner; +import source.tuner.TunerEvent.Event; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationModel; + +public class TunerModel extends AbstractTableModel implements Listener +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( TunerModel.class ); + + private static final int TUNER_CLASS = 0; + private static final int TUNER_TYPE = 1; + private static final int SAMPLE_RATE = 2; + private static final int FREQUENCY = 3; + private static final int CHANNEL_COUNT = 4; + private static final int SPECTRAL_DISPLAY = 5; + + private static final String MHZ = " MHz"; + private static final String[] COLUMNS = + { "Class", "Type", "Sample Rate", "Frequency", "Channels", "Spectrum" }; + + private List mTuners = new ArrayList<>(); + private List> mTunerEventListeners = new ArrayList<>(); + private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.00000" ); + private DecimalFormat mSampleRateFormat = new DecimalFormat( "0.000" ); + + private TunerConfigurationModel mTunerConfigurationModel; + + public TunerModel( TunerConfigurationModel tunerConfigurationModel ) + { + mTunerConfigurationModel = tunerConfigurationModel; + } + + public TunerConfigurationModel getTunerConfigurationModel() + { + return mTunerConfigurationModel; + } + + /** + * List of Tuners currently in the model + */ + public List getTuners() + { + return mTuners; + } + + public Tuner getTuner( int index ) + { + if( index < mTuners.size() ) + { + return mTuners.get( index ); + } + + return null; + } + + public void addTuners( List Tuners ) + { + for( Tuner tuner: Tuners ) + { + addTuner( tuner ); + } + } + + /** + * Adds the Tuner to this model + */ + public void addTuner( Tuner tuner ) + { + if( !mTuners.contains( tuner ) ) + { + //Get the tuner configuration and apply it to the tuner - this + //call should always produce a tuner configuration + TunerConfiguration config = mTunerConfigurationModel + .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); + + try + { + tuner.apply( config ); + + mTuners.add( tuner ); + + int index = mTuners.indexOf( tuner ); + + fireTableRowsInserted( index, index ); + + tuner.addTunerChangeListener( this ); + } + catch( SourceException se ) + { + mLog.error( "Couldn't apply tuner configuration to tuner - [" + + tuner.getTunerType().name() + "] with id [" + + tuner.getUniqueID() + "] - tuner will not be included" ); + } + + } + } + + /** + * Removes the Tuner from this model + */ + public void removeTuner( Tuner tuner ) + { + if( mTuners.contains( tuner ) ) + { + tuner.removeTunerChangeListener( this ); + + int index = mTuners.indexOf( tuner ); + + mTuners.remove( tuner ); + + fireTableRowsDeleted( index, index ); + } + } + + public void addListener( Listener listener ) + { + mTunerEventListeners.add( listener ); + } + + public void removeListener( Listener listener ) + { + mTunerEventListeners.remove( listener ); + } + + public void broadcast( TunerEvent event ) + { + for( Listener listener: mTunerEventListeners ) + { + listener.receive( event ); + } + } + + /** + * Requests to display the first tuner in this model. Invoke this method + * after all listeners have registered and tuners have been added to this + * model, in order to inform the primary display to use the first tuner. + */ + public void requestFirstTunerDisplay() + { + if( mTuners.size() > 0 ) + { + broadcast( new TunerEvent( mTuners.get( 0 ), + Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) ); + } + } + + @Override + public void receive( TunerEvent event ) + { + if( event.getTuner() != null ) + { + int index = mTuners.indexOf( event.getTuner() ); + + if( index >= 0 ) + { + switch( event.getEvent() ) + { + case CHANNEL_COUNT: + fireTableCellUpdated( index, CHANNEL_COUNT ); + break; + case FREQUENCY: + fireTableCellUpdated( index, FREQUENCY ); + break; + case SAMPLE_RATE: + fireTableCellUpdated( index, SAMPLE_RATE ); + break; + case REQUEST_MAIN_SPECTRAL_DISPLAY: + fireTableCellUpdated( index, SPECTRAL_DISPLAY ); + default: + break; + } + } + } + + broadcast( event ); + } + + @Override + public int getRowCount() + { + return mTuners.size(); + } + + @Override + public int getColumnCount() + { + return 6; + } + + @Override + public Object getValueAt( int rowIndex, int columnIndex ) + { + if( rowIndex < mTuners.size() ) + { + Tuner tuner = mTuners.get( rowIndex ); + + switch( columnIndex ) + { + case TUNER_CLASS: + return tuner.getTunerClass().getVendorDescription(); + case TUNER_TYPE: + return tuner.getTunerType().getLabel(); + case SAMPLE_RATE: + int sampleRate = tuner.getSampleRate(); + + return mSampleRateFormat.format( sampleRate / 1E6D ) + MHZ; + case FREQUENCY: + try + { + long frequency = tuner.getFrequency(); + + return mFrequencyFormat.format( frequency / 1E6D ) + MHZ; + } + catch( Exception e ) + { + return 0; + } + case CHANNEL_COUNT: + return tuner.getChannelCount(); + case SPECTRAL_DISPLAY: + return "TBD"; + default: + break; + } + } + + return null; + } + + @Override + public String getColumnName( int columnIndex ) + { + return COLUMNS[ columnIndex ]; + } + + /** + * Iterates current tuners to get a tuner channel source for the frequency + * specified in the channel config's source config object. + * + * Returns null if no tuner can source the channel + */ + public Source getSource( SourceConfigTuner config, int bandwidth ) + { + TunerChannelSource retVal = null; + + TunerChannel tunerChannel = config.getTunerChannel(); + + tunerChannel.setBandwidth( bandwidth ); + + Iterator it = mTuners.iterator(); + + Tuner tuner; + + while( it.hasNext() && retVal == null ) + { + tuner = it.next(); + + try + { + retVal = tuner.getChannel( tunerChannel ); + } + catch ( RejectedExecutionException ree ) + { + mLog.error( "couldn't provide tuner channel source", ree ); + } + catch ( SourceException e ) + { + mLog.error( "error obtaining channel from tuner [" + + tuner.getName() + "]", e ); + } + } + + return retVal; + } + +} diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java deleted file mode 100644 index fdba9a08..00000000 --- a/src/source/tuner/TunerNode.java +++ /dev/null @@ -1,166 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.DecimalFormat; - -import javax.swing.JMenuItem; -import javax.swing.JPanel; -import javax.swing.JPopupMenu; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import playlist.PlaylistManager; -import settings.SettingsManager; -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.IFrequencyChangeProcessor; -import spectrum.SpectrumFrame; -import controller.BaseNode; -import controller.channel.ChannelModel; -import controller.channel.ChannelProcessingManager; - -public class TunerNode extends BaseNode implements IFrequencyChangeProcessor -{ - private final static Logger mLog = - LoggerFactory.getLogger( TunerNode.class ); - - private static final long serialVersionUID = 1L; - private static DecimalFormat FREQUENCY_FORMAT = new DecimalFormat( "0.00000" ); - private static DecimalFormat SAMPLE_RATE_FORMAT = new DecimalFormat( "0.000" ); - - private long mFrequency; - private int mSampleRate; - private ChannelModel mChannelModel; - private ChannelProcessingManager mChannelProcessingManager; - private SettingsManager mSettingsManager; - - public TunerNode( ChannelModel channelModel, - ChannelProcessingManager channelProcessingManager, - SettingsManager settingsManager, - Tuner tuner ) - { - super( tuner ); //does this make it a super tuner? - - mChannelModel = channelModel; - mChannelProcessingManager = channelProcessingManager; - mSettingsManager = settingsManager; - - tuner.addFrequencyChangeProcessor( this ); - - try - { - mFrequency = tuner.getFrequency(); - mSampleRate = tuner.getSampleRate(); - } - catch ( SourceException e ) - { - mLog.error( "TunerNode - error reading frequency and sample rate " - + "from tuner", e ); - } - } - - @Override - public JPanel getEditor() - { - return getTuner().getEditor( mSettingsManager ); - } - - public Tuner getTuner() - { - return (Tuner)getUserObject(); - } - - public String toString() - { - StringBuilder sb = new StringBuilder(); - - sb.append( getTuner().getName() ); - - sb.append( " " ); - sb.append( FREQUENCY_FORMAT.format( (double)mFrequency / 1E6d ) ); - sb.append( " [" ); - sb.append( SAMPLE_RATE_FORMAT.format( (double)mSampleRate / 1E6d ) ); - sb.append( "] MHz" ); - - return sb.toString(); - } - - public JPopupMenu getContextMenu() - { - JPopupMenu retVal = new JPopupMenu(); - - JMenuItem showSpectrumItem = new JMenuItem( "Show in Main Spectrum" ); - showSpectrumItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - getModel().fireTunerSelectedEvent( getTuner() ); - } - } ); - - retVal.add( showSpectrumItem ); - - JMenuItem newSpectrumItem = new JMenuItem( "New Spectrum Window" ); - newSpectrumItem.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SpectrumFrame frame = new SpectrumFrame( getModel(), mChannelModel, - mChannelProcessingManager, mSettingsManager, getTuner() ); - } - } ); - - retVal.add( newSpectrumItem ); - - return retVal; - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - switch( event.getEvent() ) - { - case NOTIFICATION_FREQUENCY_CHANGE: - long frequency = event.getValue().longValue(); - - if( mFrequency != frequency ) - { - mFrequency = frequency; - getModel().nodeChanged( TunerNode.this ); - } - break; - case NOTIFICATION_SAMPLE_RATE_CHANGE: - int sampleRate = event.getValue().intValue(); - - if( mSampleRate != sampleRate ) - { - mSampleRate = sampleRate; - getModel().nodeChanged( TunerNode.this ); - } - break; - default: - break; - } - } -} diff --git a/src/source/tuner/TunerSelectionListener.java b/src/source/tuner/TunerSelectionListener.java deleted file mode 100644 index 52ebff6d..00000000 --- a/src/source/tuner/TunerSelectionListener.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner; - -public interface TunerSelectionListener -{ - public void tunerSelected( Tuner tuner ); -} diff --git a/src/source/tuner/TunerType.java b/src/source/tuner/TunerType.java index 75dc06ca..65b293f3 100644 --- a/src/source/tuner/TunerType.java +++ b/src/source/tuner/TunerType.java @@ -19,7 +19,7 @@ package source.tuner; public enum TunerType { - AIRSPY_R820T( "R820T/R820T2" ), + AIRSPY_R820T( "Airspy R820T" ), ELONICS_E4000( "E4000" ), ETTUS_WBX( "WBX" ), ETTUS_VARIOUS( "Ettus Tuner" ), diff --git a/src/source/TunerViewPanel.java b/src/source/tuner/TunerViewPanel.java similarity index 61% rename from src/source/TunerViewPanel.java rename to src/source/tuner/TunerViewPanel.java index 5b577bfb..c6a5879d 100644 --- a/src/source/TunerViewPanel.java +++ b/src/source/tuner/TunerViewPanel.java @@ -1,8 +1,5 @@ -package source; +package source.tuner; -import gui.editor.EmptyEditor; - -import java.awt.Component; import java.awt.Dimension; import javax.swing.JPanel; @@ -17,10 +14,6 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import settings.SettingsManager; -import source.tuner.Tuner; -import source.tuner.TunerManager; - import com.jidesoft.swing.JideSplitPane; public class TunerViewPanel extends JPanel @@ -29,21 +22,22 @@ public class TunerViewPanel extends JPanel private final static Logger mLog = LoggerFactory.getLogger( TunerViewPanel.class ); private TunerModel mTunerModel; - private SettingsManager mSettingsManager; - private JideSplitPane mSplitPane; private JTable mTunerTable; + + private JideSplitPane mSplitPane; + private TunerEditor mTunerEditor; - public TunerViewPanel( TunerModel tunerModel, SettingsManager settingsManager ) + public TunerViewPanel( TunerModel tunerModel ) { mTunerModel = tunerModel; - mSettingsManager = settingsManager; + mTunerEditor = new TunerEditor( mTunerModel.getTunerConfigurationModel() ); init(); } private void init() { - setLayout( new MigLayout( "insets 1 1 1 1", "[fill,grow]", "[fill,grow]" ) ); + setLayout( new MigLayout( "insets 0 0 0 0", "[fill,grow]", "[fill,grow]" ) ); mTunerTable = new JTable( mTunerModel ); mTunerTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); @@ -56,29 +50,8 @@ public class TunerViewPanel extends JPanel { int row = mTunerTable.getSelectedRow(); int modelRow = mTunerTable.convertRowIndexToModel( row ); - - Tuner selected = mTunerModel.getTuner( modelRow ); - - Component component = null; - - if( selected != null ) - { - JPanel tunerEditor = TunerManager - .getEditor( mSettingsManager, selected ); - - component = new JScrollPane( tunerEditor ); - } - else - { - component = new EmptyEditor(); - } - int split = mSplitPane.getDividerLocation( 0 ); - - mSplitPane.remove( 2 ); - mSplitPane.add( component ); - mSplitPane.setDividerLocation( 0, split ); - validate(); + mTunerEditor.setItem( mTunerModel.getTuner( modelRow ) ); } } } ); @@ -86,7 +59,7 @@ public class TunerViewPanel extends JPanel JScrollPane listScroller = new JScrollPane( mTunerTable ); listScroller.setPreferredSize( new Dimension( 400, 20 ) ); - JScrollPane editorScroller = new JScrollPane( new EmptyEditor() ); + JScrollPane editorScroller = new JScrollPane( mTunerEditor ); editorScroller.setPreferredSize( new Dimension( 400, 80 ) ); mSplitPane = new JideSplitPane(); @@ -96,4 +69,6 @@ public class TunerViewPanel extends JPanel add( mSplitPane ); } + + } diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 4637bfc8..3d1406f3 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -29,13 +29,13 @@ import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerChangeEvent; +import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.BoardID; +import source.tuner.configuration.TunerConfiguration; public class AirspyTuner extends Tuner { @@ -54,12 +54,6 @@ public class AirspyTuner extends Tuner mController.addListener( this ); } - @Override - public JPanel getEditor( SettingsManager settingsManager ) - { - return new AirspyTunerEditorPanel( this, settingsManager ); - } - public AirspyTunerController getController() { return mController; @@ -142,7 +136,7 @@ public class AirspyTuner extends Tuner if( source != null ) { - broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); } return source; diff --git a/src/source/tuner/airspy/AirspyTunerConfiguration.java b/src/source/tuner/airspy/AirspyTunerConfiguration.java index 52a3a818..5b50c348 100644 --- a/src/source/tuner/airspy/AirspyTunerConfiguration.java +++ b/src/source/tuner/airspy/AirspyTunerConfiguration.java @@ -19,9 +19,9 @@ package source.tuner.airspy; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.Gain; +import source.tuner.configuration.TunerConfiguration; public class AirspyTunerConfiguration extends TunerConfiguration { diff --git a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java index 937a63f8..781c52e2 100644 --- a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java +++ b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java @@ -47,11 +47,11 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.Gain; import source.tuner.airspy.AirspyTunerController.GainMode; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationAssignment; public class AirspyTunerConfigurationPanel extends JPanel { @@ -126,8 +126,8 @@ public class AirspyTunerConfigurationPanel extends JPanel if( serial != null ) { - savedConfig = mSettingsManager.getSelectedTunerConfiguration( - TunerType.AIRSPY_R820T, serial ); +// savedConfig = mSettingsManager.getSelectedTunerConfiguration( +// TunerType.AIRSPY_R820T, serial ); } if( savedConfig != null ) @@ -145,8 +145,8 @@ public class AirspyTunerConfigurationPanel extends JPanel if( serial != null ) { - mSettingsManager.setSelectedTunerConfiguration( TunerType.AIRSPY_R820T, - serial, mSelectedConfig ); +// mSettingsManager.setSelectedTunerConfiguration( TunerType.AIRSPY_R820T, +// serial, mSelectedConfig ); } } @@ -515,16 +515,16 @@ public class AirspyTunerConfigurationPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - TunerConfiguration config = - mSettingsManager.addNewTunerConfiguration( - TunerType.AIRSPY_R820T, - "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedItem( config ); - - repaint(); +// TunerConfiguration config = +// mSettingsManager.addNewTunerConfiguration( +// TunerType.AIRSPY_R820T, +// "New Configuration" ); +// +// mComboConfigurations.setModel( getModel() ); +// +// mComboConfigurations.setSelectedItem( config ); +// +// repaint(); } } ); @@ -555,7 +555,7 @@ public class AirspyTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mSettingsManager.deleteTunerConfiguration( selected ); +// mSettingsManager.deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -631,8 +631,8 @@ public class AirspyTunerConfigurationPanel extends JPanel mGainModeCombo.setSelectedItem( Gain.getGainMode( gain ) ); mGain.setValue( gain.getValue() ); - mSettingsManager.setSelectedTunerConfiguration( - TunerType.AIRSPY_R820T, mController.getDeviceInfo().getSerialNumber(), config ); +// mSettingsManager.setSelectedTunerConfiguration( +// TunerType.AIRSPY_R820T, mController.getDeviceInfo().getSerialNumber(), config ); save(); } @@ -656,16 +656,16 @@ public class AirspyTunerConfigurationPanel extends JPanel */ private ComboBoxModel getModel() { - ArrayList configs = - mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); +// ArrayList configs = +// mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( TunerConfiguration config: configs ) - { - model.addElement( (AirspyTunerConfiguration)config ); - } +// for( TunerConfiguration config: configs ) +// { +// model.addElement( (AirspyTunerConfiguration)config ); +// } return model; } @@ -675,16 +675,16 @@ public class AirspyTunerConfigurationPanel extends JPanel */ private AirspyTunerConfiguration getNamedConfiguration( String name ) { - ArrayList configs = - mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); +// ArrayList configs = +// mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (AirspyTunerConfiguration)config; - } - } +// for( TunerConfiguration config: configs ) +// { +// if( config.getName().contentEquals( name ) ) +// { +// return (AirspyTunerConfiguration)config; +// } +// } return null; } diff --git a/src/source/tuner/airspy/AirspyTunerController.java b/src/source/tuner/airspy/AirspyTunerController.java index fb82784d..e132e59e 100644 --- a/src/source/tuner/airspy/AirspyTunerController.java +++ b/src/source/tuner/airspy/AirspyTunerController.java @@ -28,8 +28,8 @@ import sample.Broadcaster; import sample.Listener; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerController; +import source.tuner.configuration.TunerConfiguration; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; import dsp.filter.dc.DCRemovalFilter_RB; diff --git a/src/source/tuner/TunerConfiguration.java b/src/source/tuner/configuration/TunerConfiguration.java similarity index 96% rename from src/source/tuner/TunerConfiguration.java rename to src/source/tuner/configuration/TunerConfiguration.java index d82977da..eee31f3f 100644 --- a/src/source/tuner/TunerConfiguration.java +++ b/src/source/tuner/configuration/TunerConfiguration.java @@ -15,11 +15,13 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner; +package source.tuner.configuration; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; +import source.tuner.TunerType; + /** * Abstract class to hold a named configuration for a specific type of tuner */ diff --git a/src/source/tuner/TunerConfigurationAssignment.java b/src/source/tuner/configuration/TunerConfigurationAssignment.java similarity index 96% rename from src/source/tuner/TunerConfigurationAssignment.java rename to src/source/tuner/configuration/TunerConfigurationAssignment.java index 0cb450bf..3e3fcf2e 100644 --- a/src/source/tuner/TunerConfigurationAssignment.java +++ b/src/source/tuner/configuration/TunerConfigurationAssignment.java @@ -15,10 +15,12 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ -package source.tuner; +package source.tuner.configuration; import javax.xml.bind.annotation.XmlAttribute; +import source.tuner.TunerType; + public class TunerConfigurationAssignment { private TunerType mTunerType; diff --git a/src/source/tuner/configuration/TunerConfigurationEditor.java b/src/source/tuner/configuration/TunerConfigurationEditor.java new file mode 100644 index 00000000..1c02f4b0 --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationEditor.java @@ -0,0 +1,20 @@ +package source.tuner.configuration; + +import gui.editor.Editor; + +public abstract class TunerConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private TunerConfigurationModel mTunerConfigurationModel; + + public TunerConfigurationEditor( TunerConfigurationModel model ) + { + mTunerConfigurationModel = model; + } + + public TunerConfigurationModel getTunerConfigurationModel() + { + return mTunerConfigurationModel; + } +} diff --git a/src/source/tuner/configuration/TunerConfigurationEvent.java b/src/source/tuner/configuration/TunerConfigurationEvent.java new file mode 100644 index 00000000..3815c7b7 --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationEvent.java @@ -0,0 +1,45 @@ +package source.tuner.configuration; + +public class TunerConfigurationEvent +{ + private TunerConfiguration mTunerConfiguration; + private TunerConfigurationAssignment mTunerConfigurationAssignment; + private Event mEvent; + + public TunerConfigurationEvent( TunerConfiguration configuration, Event event ) + { + mTunerConfiguration = configuration; + mEvent = event; + } + + public TunerConfigurationEvent( TunerConfigurationAssignment assignment, Event event ) + { + mTunerConfigurationAssignment = assignment; + mEvent = event; + } + + public TunerConfiguration getConfiguration() + { + return mTunerConfiguration; + } + + public TunerConfigurationAssignment getAssignment() + { + return mTunerConfigurationAssignment; + } + + public Event getEvent() + { + return mEvent; + } + + public enum Event + { + CONFIGURATION_ADD, + CONFIGURATION_REMOVE, + CONFIGURATION_CHANGE, + ASSIGNMENT_ADD, + ASSIGNMENT_REMOVE, + ASSIGNMENT_CHANGE; + } +} diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java new file mode 100644 index 00000000..2af4a4eb --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -0,0 +1,64 @@ +package source.tuner.configuration; + +import source.tuner.Tuner; +import source.tuner.TunerType; +import source.tuner.airspy.AirspyTunerConfiguration; +import source.tuner.fcd.proV1.FCD1TunerConfiguration; +import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; +import source.tuner.hackrf.HackRFTunerConfiguration; +import source.tuner.rtl.RTL2832Tuner; +import source.tuner.rtl.e4k.E4KTunerConfiguration; +import source.tuner.rtl.r820t.R820TTunerConfiguration; +import source.tuner.rtl.r820t.R820TTunerEditor; + +public class TunerConfigurationFactory +{ + public static TunerConfiguration getTunerConfiguration( TunerType type, String name ) + { + switch( type ) + { + case AIRSPY_R820T: + return new AirspyTunerConfiguration( name ); + case ELONICS_E4000: + return new E4KTunerConfiguration( name ); + case FUNCUBE_DONGLE_PRO: + return new FCD1TunerConfiguration( name ); + case FUNCUBE_DONGLE_PRO_PLUS: + return new FCD2TunerConfiguration( name ); + case HACKRF: + return new HackRFTunerConfiguration( name ); + case RAFAELMICRO_R820T: + return new R820TTunerConfiguration( name ); + default: + throw new IllegalArgumentException( "Unrecognized tuner type [" + + type.name() + "] - can't create named [" + name + "] tuner" + + " configuration" ); + } + } + + public static TunerConfigurationEditor getEditor( Tuner tuner, + TunerConfigurationModel model ) + { + switch( tuner.getTunerType() ) + { + case AIRSPY_R820T: + break; + case ELONICS_E4000: + break; + case FUNCUBE_DONGLE_PRO: + break; + case FUNCUBE_DONGLE_PRO_PLUS: + break; + case HACKRF: + break; + case RAFAELMICRO_R820T: + return new R820TTunerEditor( model, (RTL2832Tuner)tuner ); + case UNKNOWN: + default: + break; + } + + return null; + } + +} diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java new file mode 100644 index 00000000..d41bd7c7 --- /dev/null +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -0,0 +1,370 @@ +package source.tuner.configuration; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.RowFilter; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.TableRowSorter; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.tuner.TunerType; +import source.tuner.TunerViewPanel; +import source.tuner.configuration.TunerConfigurationEvent.Event; + +public class TunerConfigurationModel extends AbstractTableModel +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = + LoggerFactory.getLogger( TunerConfigurationModel.class ); + + private static final int TUNER_TYPE = 0; + private static final int CONFIGURATION_NAME = 1; + private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name" }; + + private List> mConfigurationListeners = new ArrayList<>(); + private List mTunerConfigurations = new ArrayList<>(); + private List mTunerConfigurationAssignments = new ArrayList<>(); + + /** + * Table model for managing tuner configurations and tuner configuration + * assignments. Provides tuner configuration events to registered listeners + */ + public TunerConfigurationModel() + { + } + + /** + * Adds the list of tuner configurations to the model + */ + public void addTunerConfigurations( List configs ) + { + for( TunerConfiguration config: configs ) + { + addTunerConfiguration( config ); + } + } + + /** + * Returns of list of tuner configurations from the model + */ + public List getTunerConfigurations() + { + return mTunerConfigurations; + } + + /** + * Adds the tuner configuration to the model firing a table model row add + * event and broadcasting a tuner configuration event. + */ + public void addTunerConfiguration( TunerConfiguration config ) + { + if( !mTunerConfigurations.contains( config ) ) + { + mTunerConfigurations.add( config ); + + int index = mTunerConfigurations.indexOf( config ); + + fireTableRowsInserted( index, index ); + + broadcast( new TunerConfigurationEvent( config, Event.CONFIGURATION_ADD ) ); + } + } + + /** + * Removes the tuner configuration from the model firing a table model row + * delete event and broadcasting a tuner configuration event. + */ + public void removeTunerConfiguration( TunerConfiguration config ) + { + if( mTunerConfigurations.contains( config ) ) + { + int index = mTunerConfigurations.indexOf( config ); + + mTunerConfigurations.remove( config ); + + fireTableRowsDeleted( index, index ); + + broadcast( new TunerConfigurationEvent( config, Event.CONFIGURATION_REMOVE ) ); + } + } + + /** + * Removes all tuner configurations and assignments from the model and + * fires a table clear event and broadcasts a tuner configuration event for + * each row removed. + */ + public void clear() + { + for( TunerConfiguration config: mTunerConfigurations ) + { + broadcast( new TunerConfigurationEvent( config, Event.CONFIGURATION_REMOVE ) ); + } + + int size = mTunerConfigurations.size(); + + mTunerConfigurations.clear(); + + fireTableRowsDeleted( 0, size - 1 ); + + for( TunerConfigurationAssignment assignment: mTunerConfigurationAssignments ) + { + broadcast( new TunerConfigurationEvent( assignment, Event.ASSIGNMENT_REMOVE ) ); + } + + mTunerConfigurationAssignments.clear(); + } + + /** + * Registers a tuner configuration event listener + */ + public void addListener( Listener listener ) + { + mConfigurationListeners.add( listener ); + } + + /** + * Removes the tuner configuration event listener + */ + public void removeListener( Listener listener ) + { + mConfigurationListeners.remove( listener ); + } + + /** + * Broadcasts a tuner configuration event + */ + public void broadcast( TunerConfigurationEvent event ) + { + for( Listener listener: mConfigurationListeners ) + { + listener.receive( event ); + } + } + + /** + * Adds a list of tuner configuration assignments to the model + */ + public void addTunerConfigurationAssignments( List assignments ) + { + for( TunerConfigurationAssignment assigment: assignments ) + { + addTunerConfigurationAssignment( assigment ); + } + } + + /** + * Returns of list of tuner configurations from the model + */ + public List getTunerConfigurationAssignments() + { + return mTunerConfigurationAssignments; + } + + /** + * Adds a tuner configuration assigment to the model + */ + public void addTunerConfigurationAssignment( TunerConfigurationAssignment assignment ) + { + mTunerConfigurationAssignments.add( assignment ); + + broadcast( new TunerConfigurationEvent( assignment, Event.ASSIGNMENT_ADD ) ); + } + + /** + * Removes a tuner configuration assignment from the model + */ + public void removeTunerConfigurationAssignment( TunerConfigurationAssignment assignment ) + { + mTunerConfigurationAssignments.remove( assignment ); + + broadcast( new TunerConfigurationEvent( assignment, Event.ASSIGNMENT_REMOVE ) ); + } + + /** + * Returns the tuner configuration that is assigned to the tuner with the + * tuner type and uniqueID, or creates a new tuner configuration and tuner + * configuration assignment for the tuner. + */ + public TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID ) + { + String configurationName = null; + + TunerConfigurationAssignment assignment = getTunerConfigurationAssignment( type, uniqueID ); + + if( assignment != null ) + { + configurationName = assignment.getTunerConfigurationName(); + } + + if( configurationName == null ) + { + configurationName = "Default-" + uniqueID; + } + + List configs = getTunerConfigurations( type ); + + for( TunerConfiguration config: configs ) + { + if( config.getName().contentEquals( configurationName ) ) + { + if( assignment == null ) + { + assignTunerConfiguration( config, configurationName ); + } + + return config; + } + } + + //We didn't find the config so create a new one and assign it + TunerConfiguration config = TunerConfigurationFactory + .getTunerConfiguration( type, configurationName ); + + addTunerConfiguration( config ); + assignTunerConfiguration( config, configurationName ); + + return config; + } + + /** + * Assigns the tuner configuration to the tuner identified by the uniqueID + */ + public void assignTunerConfiguration( TunerConfiguration config, String uniqueID ) + { + TunerConfigurationAssignment assignment = + getTunerConfigurationAssignment( config.getTunerType(), uniqueID ); + + Event event = Event.ASSIGNMENT_CHANGE; + + if( assignment == null ) + { + assignment = new TunerConfigurationAssignment(); + assignment.setUniqueID( uniqueID ); + + addTunerConfigurationAssignment( assignment ); + + event = Event.ASSIGNMENT_ADD; + } + + assignment.setTunerConfigurationName( config.getName() ); + + broadcast( new TunerConfigurationEvent( assignment, event ) ); + } + + /** + * Returns the tuner configuration assiged to the tuner type and unique id + */ + private TunerConfigurationAssignment getTunerConfigurationAssignment( TunerType type, String uniqueID ) + { + for( TunerConfigurationAssignment assignment: mTunerConfigurationAssignments ) + { + if( assignment.getTunerType() == type && + assignment.getUniqueID().contentEquals( uniqueID ) ) + { + return assignment; + } + } + + return null; + } + + /** + * Returns all tuner configurations for the specified tuner type + */ + public List getTunerConfigurations( TunerType type ) + { + List configs = new ArrayList<>(); + + for( TunerConfiguration config: mTunerConfigurations ) + { + if( config.getTunerType() == type ) + { + configs.add( config ); + } + } + + return configs; + } + + /** + * Column names for the table model + */ + @Override + public String getColumnName( int column ) + { + return COLUMN_HEADERS[ column ]; + } + + /** + * Tuner configuration model row count + */ + @Override + public int getRowCount() + { + return mTunerConfigurations.size(); + } + + /** + * Tuner configuration model column count + */ + @Override + public int getColumnCount() + { + return COLUMN_HEADERS.length; + } + + /** + * Returns the row index for the specified configuration or -1 + */ + public int getRowIndex( TunerConfiguration configuration ) + { + if( mTunerConfigurations.contains( configuration ) ) + { + return mTunerConfigurations.indexOf( configuration ); + } + + return -1; + } + + /** + * Returns the configuration at the specified index or null + */ + public TunerConfiguration getTunerConfiguration( int index ) + { + if( index >= 0 && index < mTunerConfigurations.size() ) + { + return mTunerConfigurations.get( index ); + } + + return null; + } + + /** + * Returns the value at the indicated table cell + */ + @Override + public Object getValueAt( int row, int column ) + { + if( row >= 0 && row < mTunerConfigurations.size() ) + { + TunerConfiguration config = mTunerConfigurations.get( row ); + + switch( column ) + { + case TUNER_TYPE: + return config.getTunerType().getLabel(); + case CONFIGURATION_NAME: + return config.getName(); + default: + break; + } + } + + return null; + } + +} diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java index b14e0e77..40e4c6b8 100644 --- a/src/source/tuner/ettus/B100Tuner.java +++ b/src/source/tuner/ettus/B100Tuner.java @@ -30,8 +30,8 @@ import source.tuner.Tuner; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.usb.USBTunerDevice; import controller.ThreadPoolManager; @@ -68,12 +68,6 @@ public class B100Tuner extends Tuner return TunerType.ETTUS_WBX; } - @Override - public JPanel getEditor( SettingsManager settingsManager ) - { - return new B100TunerEditorPanel( this, settingsManager ); - } - @Override public void apply( TunerConfiguration config ) throws SourceException { diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index e2a1bcfa..9ce5087b 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -28,12 +28,12 @@ import settings.SettingsManager; import source.SourceException; import source.tuner.MixerTuner; import source.tuner.MixerTunerDataLine; -import source.tuner.TunerChangeEvent; +import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; public class FCDTuner extends MixerTuner { @@ -80,12 +80,6 @@ public class FCDTuner extends MixerTuner return mController.getUSBAddress(); } - @Override - public JPanel getEditor( SettingsManager settingsManager ) - { - return mController.getEditor( this, settingsManager ); - } - @Override public void apply( TunerConfiguration config )throws SourceException { @@ -118,7 +112,7 @@ public class FCDTuner extends MixerTuner if( source != null ) { - broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); } return source; diff --git a/src/source/tuner/fcd/FCDTunerController.java b/src/source/tuner/fcd/FCDTunerController.java index ca602ed2..d1fdfd62 100644 --- a/src/source/tuner/fcd/FCDTunerController.java +++ b/src/source/tuner/fcd/FCDTunerController.java @@ -36,9 +36,9 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerController; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.proV1.FCD1TunerController.Block; public abstract class FCDTunerController extends TunerController diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java index 29a7b0f4..43152221 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.fcd.proV1; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java index b54bef0b..051d597f 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java @@ -49,8 +49,8 @@ import org.slf4j.LoggerFactory; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; @@ -426,14 +426,14 @@ public class FCD1TunerConfigurationPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - TunerConfiguration config = - mSettingsManager.addNewTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO, - "New Configuration" ); +// TunerConfiguration config = +// mSettingsManager.addNewTunerConfiguration( +// TunerType.FUNCUBE_DONGLE_PRO, +// "New Configuration" ); mComboConfigurations.setModel( getModel() ); - mComboConfigurations.setSelectedItem( config ); +// mComboConfigurations.setSelectedItem( config ); repaint(); } @@ -470,7 +470,7 @@ public class FCD1TunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mSettingsManager.deleteTunerConfiguration( selected ); +// mSettingsManager.deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -505,9 +505,9 @@ public class FCD1TunerConfigurationPanel extends JPanel mCorrectionGain.setValue( config.getGainCorrection() ); mCorrectionPhase.setValue( config.getPhaseCorrection() ); - mSettingsManager.setSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO, - mController.getUSBAddress(), config ); +// mSettingsManager.setSelectedTunerConfiguration( +// TunerType.FUNCUBE_DONGLE_PRO, +// mController.getUSBAddress(), config ); } catch ( SourceException e1 ) { @@ -524,16 +524,16 @@ public class FCD1TunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { - ArrayList configs = - mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); +// ArrayList configs = +// mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( TunerConfiguration config: configs ) - { - model.addElement( (FCD1TunerConfiguration)config ); - } +// for( TunerConfiguration config: configs ) +// { +// model.addElement( (FCD1TunerConfiguration)config ); +// } return model; } @@ -541,16 +541,16 @@ public class FCD1TunerConfigurationPanel extends JPanel @SuppressWarnings( "unused" ) private FCD1TunerConfiguration getNamedConfiguration( String name ) { - ArrayList configs = - mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); +// ArrayList configs = +// mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (FCD1TunerConfiguration)config; - } - } +// for( TunerConfiguration config: configs ) +// { +// if( config.getName().contentEquals( name ) ) +// { +// return (FCD1TunerConfiguration)config; +// } +// } return null; } diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index 8de3053c..005950b1 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -32,8 +32,8 @@ import org.usb4java.DeviceDescriptor; import settings.SettingsManager; import source.SourceException; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.FCDCommand; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java index 06a4de4d..b2955570 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.fcd.proplusV2; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; public class FCD2TunerConfiguration extends TunerConfiguration { diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java index 8bd20ac9..991d0b11 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java @@ -48,9 +48,9 @@ import org.slf4j.LoggerFactory; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationAssignment; public class FCD2TunerConfigurationPanel extends JPanel { @@ -218,26 +218,26 @@ public class FCD2TunerConfigurationPanel extends JPanel * Lookup the save config and apply that config to update all of the * controls */ - TunerConfigurationAssignment savedConfig = mSettingsManager - .getSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - mController.getUSBAddress() ); - - if( savedConfig != null ) - { - mSelectedConfig = getNamedConfiguration( - savedConfig.getTunerConfigurationName() ); - } +// TunerConfigurationAssignment savedConfig = mSettingsManager +// .getSelectedTunerConfiguration( +// TunerType.FUNCUBE_DONGLE_PRO_PLUS, +// mController.getUSBAddress() ); +// +// if( savedConfig != null ) +// { +// mSelectedConfig = getNamedConfiguration( +// savedConfig.getTunerConfigurationName() ); +// } if( mSelectedConfig == null ) { mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - //Store this config as the default for this tuner at this address - mSettingsManager - .setSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - mController.getUSBAddress(), mSelectedConfig ); +// //Store this config as the default for this tuner at this address +// mSettingsManager +// .setSelectedTunerConfiguration( +// TunerType.FUNCUBE_DONGLE_PRO_PLUS, +// mController.getUSBAddress(), mSelectedConfig ); } mComboConfigurations.setSelectedItem( mSelectedConfig ); @@ -252,15 +252,15 @@ public class FCD2TunerConfigurationPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - TunerConfiguration config = - mSettingsManager - .addNewTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - "New Configuration" ); +// TunerConfiguration config = +// mSettingsManager +// .addNewTunerConfiguration( +// TunerType.FUNCUBE_DONGLE_PRO_PLUS, +// "New Configuration" ); mComboConfigurations.setModel( getModel() ); - mComboConfigurations.setSelectedItem( config ); +// mComboConfigurations.setSelectedItem( config ); repaint(); } @@ -293,8 +293,8 @@ public class FCD2TunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mSettingsManager - .deleteTunerConfiguration( selected ); +// mSettingsManager +// .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -322,9 +322,9 @@ public class FCD2TunerConfigurationPanel extends JPanel mMixerGain.setSelected( config.getGainMixer() ); mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mSettingsManager.setSelectedTunerConfiguration( - TunerType.FUNCUBE_DONGLE_PRO_PLUS, - mController.getUSBAddress(), config ); +// mSettingsManager.setSelectedTunerConfiguration( +// TunerType.FUNCUBE_DONGLE_PRO_PLUS, +// mController.getUSBAddress(), config ); } catch ( SourceException e1 ) { @@ -341,34 +341,34 @@ public class FCD2TunerConfigurationPanel extends JPanel private ComboBoxModel getModel() { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( TunerConfiguration config: configs ) - { - model.addElement( (FCD2TunerConfiguration)config ); - } +// for( TunerConfiguration config: configs ) +// { +// model.addElement( (FCD2TunerConfiguration)config ); +// } return model; } private FCD2TunerConfiguration getNamedConfiguration( String name ) { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (FCD2TunerConfiguration)config; - } - } +// for( TunerConfiguration config: configs ) +// { +// if( config.getName().contentEquals( name ) ) +// { +// return (FCD2TunerConfiguration)config; +// } +// } return null; } diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java index 96a31b85..da7520c1 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -30,8 +30,8 @@ import org.usb4java.DeviceDescriptor; import settings.SettingsManager; import source.SourceException; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.FCDCommand; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java index aee8570b..a0a52613 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -29,12 +29,12 @@ import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerChangeEvent; +import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.hackrf.HackRFTunerController.BoardID; public class HackRFTuner extends Tuner @@ -75,12 +75,6 @@ public class HackRFTuner extends Tuner return TunerType.HACKRF; } - @Override - public JPanel getEditor( SettingsManager settingsManager ) - { - return new HackRFTunerEditorPanel( this, settingsManager ); - } - @Override public void apply( TunerConfiguration config ) throws SourceException { @@ -113,7 +107,7 @@ public class HackRFTuner extends Tuner if( source != null ) { - broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); } return source; diff --git a/src/source/tuner/hackrf/HackRFTunerConfiguration.java b/src/source/tuner/hackrf/HackRFTunerConfiguration.java index 66d85e25..33356fc0 100644 --- a/src/source/tuner/hackrf/HackRFTunerConfiguration.java +++ b/src/source/tuner/hackrf/HackRFTunerConfiguration.java @@ -20,8 +20,8 @@ package source.tuner.hackrf; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; diff --git a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java index 1acd7269..88aa95d6 100644 --- a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java +++ b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java @@ -50,9 +50,9 @@ import org.slf4j.LoggerFactory; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; @@ -117,11 +117,11 @@ public class HackRFTunerConfigurationPanel extends JPanel mLog.error( "couldn't read hackrf serial number", e2 ); } - if( serial != null ) - { - savedConfig = mSettingsManager.getSelectedTunerConfiguration( - TunerType.HACKRF, serial ); - } +// if( serial != null ) +// { +// savedConfig = mSettingsManager.getSelectedTunerConfiguration( +// TunerType.HACKRF, serial ); +// } if( savedConfig != null ) { @@ -136,12 +136,12 @@ public class HackRFTunerConfigurationPanel extends JPanel //Store this config as the default for this tuner at this address - if( serial != null ) - { - mSettingsManager - .setSelectedTunerConfiguration( TunerType.HACKRF, - serial, mSelectedConfig ); - } +// if( serial != null ) +// { +// mSettingsManager +// .setSelectedTunerConfiguration( TunerType.HACKRF, +// serial, mSelectedConfig ); +// } } mComboConfigurations.setSelectedItem( mSelectedConfig ); @@ -416,15 +416,15 @@ public class HackRFTunerConfigurationPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - TunerConfiguration config = - mSettingsManager - .addNewTunerConfiguration( - TunerType.HACKRF, - "New Configuration" ); +// TunerConfiguration config = +// mSettingsManager +// .addNewTunerConfiguration( +// TunerType.HACKRF, +// "New Configuration" ); mComboConfigurations.setModel( getModel() ); - mComboConfigurations.setSelectedItem( config ); +// mComboConfigurations.setSelectedItem( config ); repaint(); } @@ -457,8 +457,8 @@ public class HackRFTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mSettingsManager - .deleteTunerConfiguration( selected ); +// mSettingsManager +// .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -493,10 +493,10 @@ public class HackRFTunerConfigurationPanel extends JPanel mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mSettingsManager.setSelectedTunerConfiguration( - TunerType.HACKRF, mController.getSerial().getSerialNumber(), config ); +// mSettingsManager.setSelectedTunerConfiguration( +// TunerType.HACKRF, mController.getSerial().getSerialNumber(), config ); } - catch ( UsbException | SourceException e1 ) + catch ( SourceException e1 ) { JOptionPane.showMessageDialog( HackRFTunerConfigurationPanel.this, @@ -516,17 +516,17 @@ public class HackRFTunerConfigurationPanel extends JPanel */ private ComboBoxModel getModel() { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.HACKRF ); +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.HACKRF ); DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( TunerConfiguration config: configs ) - { - model.addElement( (HackRFTunerConfiguration)config ); - } +// for( TunerConfiguration config: configs ) +// { +// model.addElement( (HackRFTunerConfiguration)config ); +// } return model; } @@ -536,17 +536,17 @@ public class HackRFTunerConfigurationPanel extends JPanel */ private HackRFTunerConfiguration getNamedConfiguration( String name ) { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.HACKRF ); +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.HACKRF ); - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (HackRFTunerConfiguration)config; - } - } +// for( TunerConfiguration config: configs ) +// { +// if( config.getName().contentEquals( name ) ) +// { +// return (HackRFTunerConfiguration)config; +// } +// } return null; } diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java index cf4146d1..a4545dc2 100644 --- a/src/source/tuner/hackrf/HackRFTunerController.java +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -81,8 +81,8 @@ import sample.Listener; import sample.adapter.ByteSampleAdapter; import sample.complex.ComplexBuffer; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerController; +import source.tuner.configuration.TunerConfiguration; import controller.ThreadPoolManager; import controller.ThreadPoolManager.ThreadType; diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 55c7dfed..1cbbd6ce 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -30,12 +30,12 @@ import sample.complex.ComplexBuffer; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerChangeEvent; +import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.frequency.IFrequencyChangeListener; import source.tuner.rtl.RTL2832TunerController.SampleRate; import controller.ThreadPoolManager; @@ -89,12 +89,6 @@ public class RTL2832Tuner extends Tuner return mController.getTunerType(); } - @Override - public JPanel getEditor( SettingsManager settingsManager ) - { - return mController.getEditor( settingsManager ); - } - @Override public void apply( TunerConfiguration config ) throws SourceException { @@ -161,7 +155,7 @@ public class RTL2832Tuner extends Tuner if( source != null ) { - broadcast( new TunerChangeEvent( this, TunerChangeEvent.Event.CHANNEL_COUNT ) ); + broadcast( new TunerEvent( this, TunerEvent.Event.CHANNEL_COUNT ) ); } } catch( Exception e ) diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index 802bd15b..5efa45d4 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -363,8 +363,6 @@ public abstract class RTL2832TunerController extends TunerController } } - public abstract JPanel getEditor( SettingsManager settingsManager ); - public abstract void setSampleRateFilters( int sampleRate ) throws SourceException; diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java index d7d82a62..5c699d4d 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.rtl.e4k; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; import source.tuner.rtl.e4k.E4KTunerController.E4KGain; diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java index ed3a4ac7..b411ff5e 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java @@ -50,9 +50,9 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; import source.tuner.rtl.e4k.E4KTunerController.E4KGain; @@ -108,15 +108,15 @@ public class E4KTunerConfigurationPanel extends JPanel mComboConfigurations.setModel( getModel() ); /* Determine which tuner configuration should be selected/displayed */ - TunerConfigurationAssignment savedConfig = mSettingsManager - .getSelectedTunerConfiguration( - TunerType.ELONICS_E4000, mController.getUniqueID() ); - - if( savedConfig != null ) - { - mSelectedConfig = getNamedConfiguration( - savedConfig.getTunerConfigurationName() ); - } +// TunerConfigurationAssignment savedConfig = mSettingsManager +// .getSelectedTunerConfiguration( +// TunerType.ELONICS_E4000, mController.getUniqueID() ); +// +// if( savedConfig != null ) +// { +// mSelectedConfig = getNamedConfiguration( +// savedConfig.getTunerConfigurationName() ); +// } /* If we couldn't determine the saved/selected config, use the first one */ if( mSelectedConfig == null ) @@ -124,10 +124,10 @@ public class E4KTunerConfigurationPanel extends JPanel mSelectedConfig = mComboConfigurations.getItemAt( 0 ); //Store this config as the default for this tuner at this address - mSettingsManager - .setSelectedTunerConfiguration( - TunerType.ELONICS_E4000, - mController.getUniqueID(), mSelectedConfig ); +// mSettingsManager +// .setSelectedTunerConfiguration( +// TunerType.ELONICS_E4000, +// mController.getUniqueID(), mSelectedConfig ); } mComboConfigurations.setSelectedItem( mSelectedConfig ); @@ -168,10 +168,10 @@ public class E4KTunerConfigurationPanel extends JPanel /* Reset this named config as the assigned config for this * tuner */ - mSettingsManager - .setSelectedTunerConfiguration( - TunerType.ELONICS_E4000, - mController.getUniqueID(), mSelectedConfig ); +// mSettingsManager +// .setSelectedTunerConfiguration( +// TunerType.ELONICS_E4000, +// mController.getUniqueID(), mSelectedConfig ); save(); } @@ -541,15 +541,15 @@ public class E4KTunerConfigurationPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - TunerConfiguration config = - mSettingsManager - .addNewTunerConfiguration( - TunerType.ELONICS_E4000, - "New Configuration" ); +// TunerConfiguration config = +// mSettingsManager +// .addNewTunerConfiguration( +// TunerType.ELONICS_E4000, +// "New Configuration" ); mComboConfigurations.setModel( getModel() ); - mComboConfigurations.setSelectedItem( config ); +// mComboConfigurations.setSelectedItem( config ); repaint(); } @@ -582,8 +582,8 @@ public class E4KTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mSettingsManager - .deleteTunerConfiguration( selected ); +// mSettingsManager +// .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -623,8 +623,8 @@ public class E4KTunerConfigurationPanel extends JPanel mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mSettingsManager.setSelectedTunerConfiguration( - TunerType.ELONICS_E4000, mController.getUniqueID(), config ); +// mSettingsManager.setSelectedTunerConfiguration( +// TunerType.ELONICS_E4000, mController.getUniqueID(), config ); } catch ( SourceException e1 ) { @@ -646,17 +646,17 @@ public class E4KTunerConfigurationPanel extends JPanel */ private ComboBoxModel getModel() { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.ELONICS_E4000 ); +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.ELONICS_E4000 ); DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( TunerConfiguration config: configs ) - { - model.addElement( (E4KTunerConfiguration)config ); - } +// for( TunerConfiguration config: configs ) +// { +// model.addElement( (E4KTunerConfiguration)config ); +// } return model; } @@ -666,17 +666,17 @@ public class E4KTunerConfigurationPanel extends JPanel */ private E4KTunerConfiguration getNamedConfiguration( String name ) { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.ELONICS_E4000 ); - - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (E4KTunerConfiguration)config; - } - } +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.ELONICS_E4000 ); +// +// for( TunerConfiguration config: configs ) +// { +// if( config.getName().contentEquals( name ) ) +// { +// return (E4KTunerConfiguration)config; +// } +// } return null; } diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index b727fa74..6f4df48c 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -32,8 +32,8 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController; import controller.ThreadPoolManager; diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java index 8ecc7795..95686f66 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java @@ -19,8 +19,8 @@ package source.tuner.rtl.r820t; import javax.xml.bind.annotation.XmlAttribute; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.r820t.R820TTunerController.R820TGain; import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java similarity index 88% rename from src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java rename to src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java index 413dc8ea..1a310ba1 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java @@ -50,21 +50,22 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; -import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.r820t.R820TTunerController.R820TGain; import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; -public class R820TTunerConfigurationPanel extends JPanel +public class R820TTunerConfigurationEditorOld extends JPanel { - private final static Logger mLog = - LoggerFactory.getLogger( R820TTunerConfigurationPanel.class ); - private static final long serialVersionUID = 1L; + + private final static Logger mLog = + LoggerFactory.getLogger( R820TTunerConfigurationEditorOld.class ); + private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; private SettingsManager mSettingsManager; @@ -86,7 +87,7 @@ public class R820TTunerConfigurationPanel extends JPanel private JComboBox mComboSampleRate; - public R820TTunerConfigurationPanel( SettingsManager settingsManager, + public R820TTunerConfigurationEditorOld( SettingsManager settingsManager, R820TTunerController controller ) { mSettingsManager = settingsManager; @@ -110,27 +111,27 @@ public class R820TTunerConfigurationPanel extends JPanel mComboConfigurations.setModel( getModel() ); /* Determine which tuner configuration should be selected/displayed */ - TunerConfigurationAssignment savedConfig = mSettingsManager - .getSelectedTunerConfiguration( - TunerType.RAFAELMICRO_R820T, mController.getUniqueID() ); - - if( savedConfig != null ) - { - mSelectedConfig = getNamedConfiguration( - savedConfig.getTunerConfigurationName() ); - } +// TunerConfigurationAssignment savedConfig = mSettingsManager +// .getSelectedTunerConfiguration( +// TunerType.RAFAELMICRO_R820T, mController.getUniqueID() ); +// +// if( savedConfig != null ) +// { +// mSelectedConfig = getNamedConfiguration( +// savedConfig.getTunerConfigurationName() ); +// } /* If we couldn't determine the saved/selected config, use the first one */ - if( mSelectedConfig == null ) - { - mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - - //Store this config as the default for this tuner at this address - mSettingsManager - .setSelectedTunerConfiguration( - TunerType.RAFAELMICRO_R820T, - mController.getUniqueID(), mSelectedConfig ); - } +// if( mSelectedConfig == null ) +// { +// mSelectedConfig = mComboConfigurations.getItemAt( 0 ); +// +// //Store this config as the default for this tuner at this address +// mSettingsManager +// .setSelectedTunerConfiguration( +// TunerType.RAFAELMICRO_R820T, +// mController.getUniqueID(), mSelectedConfig ); +// } mComboConfigurations.setSelectedItem( mSelectedConfig ); @@ -217,7 +218,7 @@ public class R820TTunerConfigurationPanel extends JPanel catch ( SourceException e1 ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't apply " + "frequency correction value: " + value + e1.getLocalizedMessage() ); @@ -256,7 +257,7 @@ public class R820TTunerConfigurationPanel extends JPanel catch ( SourceException | LibUsbException eSampleRate ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't apply the sample " + "rate setting [" + sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage() ); @@ -380,7 +381,7 @@ public class R820TTunerConfigurationPanel extends JPanel catch ( UsbException e ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't apply the gain " + "setting - " + e.getLocalizedMessage() ); @@ -431,7 +432,7 @@ public class R820TTunerConfigurationPanel extends JPanel catch ( UsbException e ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't apply the mixer " + "gain setting - " + e.getLocalizedMessage() ); @@ -477,7 +478,7 @@ public class R820TTunerConfigurationPanel extends JPanel catch ( UsbException e ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't apply the LNA " + "gain setting - " + e.getLocalizedMessage() ); @@ -521,7 +522,7 @@ public class R820TTunerConfigurationPanel extends JPanel catch ( UsbException e ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't apply the VGA " + "gain setting - " + e.getLocalizedMessage() ); @@ -548,17 +549,17 @@ public class R820TTunerConfigurationPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - TunerConfiguration config = - mSettingsManager - .addNewTunerConfiguration( - TunerType.RAFAELMICRO_R820T, - "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedItem( config ); - - repaint(); +// TunerConfiguration config = +// mSettingsManager +// .addNewTunerConfiguration( +// TunerType.RAFAELMICRO_R820T, +// "New Configuration" ); +// +// mComboConfigurations.setModel( getModel() ); +// +// mComboConfigurations.setSelectedItem( config ); +// +// repaint(); } } ); @@ -581,7 +582,7 @@ public class R820TTunerConfigurationPanel extends JPanel if( selected != null ) { int n = JOptionPane.showConfirmDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "Are you sure you want to delete '" + selected.getName() + "'?", "Are you sure?", @@ -589,8 +590,8 @@ public class R820TTunerConfigurationPanel extends JPanel if( n == JOptionPane.YES_OPTION ) { - mSettingsManager - .deleteTunerConfiguration( selected ); +// mSettingsManager +// .deleteTunerConfiguration( selected ); mComboConfigurations.setModel( getModel() ); @@ -630,13 +631,13 @@ public class R820TTunerConfigurationPanel extends JPanel mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mSettingsManager.setSelectedTunerConfiguration( - TunerType.RAFAELMICRO_R820T, mController.getUniqueID(), config ); +// mSettingsManager.setSelectedTunerConfiguration( +// TunerType.RAFAELMICRO_R820T, mController.getUniqueID(), config ); } catch ( SourceException e1 ) { JOptionPane.showMessageDialog( - R820TTunerConfigurationPanel.this, + R820TTunerConfigurationEditorOld.this, "R820T Tuner Controller - couldn't " + "apply the tuner configuration settings - " + e1.getLocalizedMessage() ); @@ -653,17 +654,17 @@ public class R820TTunerConfigurationPanel extends JPanel */ private ComboBoxModel getModel() { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); DefaultComboBoxModel model = new DefaultComboBoxModel(); - for( TunerConfiguration config: configs ) - { - model.addElement( (R820TTunerConfiguration)config ); - } +// for( TunerConfiguration config: configs ) +// { +// model.addElement( (R820TTunerConfiguration)config ); +// } return model; } @@ -673,17 +674,17 @@ public class R820TTunerConfigurationPanel extends JPanel */ private R820TTunerConfiguration getNamedConfiguration( String name ) { - ArrayList configs = - mSettingsManager - .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); - - for( TunerConfiguration config: configs ) - { - if( config.getName().contentEquals( name ) ) - { - return (R820TTunerConfiguration)config; - } - } +// ArrayList configs = +// mSettingsManager +// .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); +// +// for( TunerConfiguration config: configs ) +// { +// if( config.getName().contentEquals( name ) ) +// { +// return (R820TTunerConfiguration)config; +// } +// } return null; } diff --git a/src/source/tuner/rtl/r820t/R820TTunerController.java b/src/source/tuner/rtl/r820t/R820TTunerController.java index 109458e2..766c1091 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerController.java +++ b/src/source/tuner/rtl/r820t/R820TTunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * Java port of librtlsdr * @@ -33,10 +33,9 @@ import org.usb4java.Device; import org.usb4java.DeviceDescriptor; import org.usb4java.LibUsbException; -import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerConfiguration; import source.tuner.TunerType; +import source.tuner.configuration.TunerConfiguration; import source.tuner.rtl.RTL2832TunerController; import controller.ThreadPoolManager; @@ -136,19 +135,6 @@ public class R820TTunerController extends RTL2832TunerController } } - /** - * Controller gui editor component for this tuner - */ - public JPanel getEditor( SettingsManager settingsManager ) - { - if( mEditor == null ) - { - mEditor = new R820TTunerEditorPanel( this, settingsManager ); - } - - return mEditor; - } - /** * Not implemented. */ diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java new file mode 100644 index 00000000..0d106f8a --- /dev/null +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -0,0 +1,446 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source.tuner.rtl.r820t; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; + +import javax.swing.BorderFactory; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.usb.UsbException; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.LibUsbException; + +import source.SourceException; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationEvent.Event; +import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.rtl.RTL2832Tuner; +import source.tuner.rtl.RTL2832TunerController.SampleRate; +import source.tuner.rtl.r820t.R820TTunerController.R820TGain; +import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; +import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; +import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; + +public class R820TTunerEditor extends TunerConfigurationEditor +{ + private final static Logger mLog = + LoggerFactory.getLogger( R820TTunerEditor.class ); + + private static final long serialVersionUID = 1L; + private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; + + private R820TTunerController mController; + + private JTextField mConfigurationName; + private JSpinner mFrequencyCorrection; + private JComboBox mComboMasterGain; + private JComboBox mComboMixerGain; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; + private JComboBox mComboSampleRate; + + public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, + RTL2832Tuner tuner ) + { + super( tunerConfigurationModel ); + mController = (R820TTunerController)tuner.getController(); + + init(); + } + + @Override + public void save() + { + if( hasItem() ) + { + R820TTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); + + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); + config.setMasterGain( gain ); + + R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); + config.setMixerGain( mixerGain ); + + R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); + config.setLNAGain( lnaGain ); + + R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); + config.setVGAGain( vgaGain ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CONFIGURATION_CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration item ) + { + super.setItem( item ); + + if( hasItem() ) + { + R820TTunerConfiguration config = getConfiguration(); + + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + } + + private R820TTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (R820TTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow]" ) ); + + add( new JLabel( "R820T Tuner" ), "span, align center" ); + + /** + * Tuner Configuration Name + */ + mConfigurationName = new JTextField(); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Configuration:" ) ); + add( mConfigurationName, "growx,push" ); + + /** + * Frequency Correction + */ + SpinnerModel model = + new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + } + catch ( SourceException e1 ) + { + mLog.error( "Error setting frequency correction value", e1 ); + } + + save(); + } + } ); + + add( new JLabel( "Correction PPM:" ) ); + add( mFrequencyCorrection, "growx,push" ); + + /** + * Sample Rate + */ + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = + (SampleRate)mComboSampleRate.getSelectedItem(); + try + { + + mController.setSampleRate( sampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the sample " + + "rate setting [" + sampleRate.getLabel() + "] " + + eSampleRate.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply sample " + + "rate setting [" + sampleRate.getLabel() + "]", + eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate, "growx,push" ); + + /** + * Gain Controls + */ + JPanel gainPanel = new JPanel(); + gainPanel.setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); + gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); + + /* Master Gain Control */ + mComboMasterGain = new JComboBox( R820TGain.values() ); + + mComboMasterGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); + + mController.setGain( (R820TGain)mComboMasterGain.getSelectedItem(), true ); + + if( gain == R820TGain.MANUAL ) + { + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + mComboMixerGain.setEnabled( true ); + + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + mComboLNAGain.setEnabled( true ); + + mComboVGAGain.setSelectedItem( gain.getVGAGain() ); + mComboVGAGain.setEnabled( true ); + } + else + { + mComboMixerGain.setEnabled( false ); + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + + mComboVGAGain.setEnabled( false ); + mComboVGAGain.setSelectedItem( gain.getVGAGain() ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the gain " + + "setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply " + + "gain setting - ", e ); + } + } + } ); + mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " + + "gain, MANUAL to enable
    independent control of " + + "Mixer, LNA and Enhance gain
    " + + "settings, or one of the individual gain settings for
    " + + "semi-manual gain control" ); + gainPanel.add( new JLabel( "Master" ) ); + gainPanel.add( mComboMasterGain ); + + /* Mixer Gain Control */ + mComboMixerGain = new JComboBox( R820TMixerGain.values() ); + mComboMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + try + { + R820TMixerGain mixerGain = + (R820TMixerGain)mComboMixerGain.getSelectedItem(); + + if( mixerGain == null ) + { + mixerGain = DEFAULT_GAIN.getMixerGain(); + } + + if( mComboMixerGain.isEnabled() ) + { + mController.setMixerGain( mixerGain, true ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the mixer " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply mixer " + + "gain setting - ", e ); + } + } + } ); + } + } ); + mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + + "to MASTER to enable adjustment" ); + mComboMixerGain.setEnabled( false ); + gainPanel.add( new JLabel( "Mixer" ) ); + gainPanel.add( mComboMixerGain, "wrap" ); + + /* LNA Gain Control */ + mComboLNAGain = new JComboBox( R820TLNAGain.values() ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TLNAGain lnaGain = + (R820TLNAGain)mComboLNAGain.getSelectedItem(); + + if ( lnaGain == null ) + { + lnaGain = DEFAULT_GAIN.getLNAGain(); + } + + if( mComboLNAGain.isEnabled() ) + { + mController.setLNAGain( lnaGain, true ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the LNA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply LNA " + + "gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboLNAGain.setEnabled( false ); + gainPanel.add( new JLabel( "LNA" ) ); + gainPanel.add( mComboLNAGain ); + + /* VGA Gain Control */ + mComboVGAGain = new JComboBox( R820TVGAGain.values() ); + mComboVGAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + R820TVGAGain vgaGain = + (R820TVGAGain)mComboVGAGain.getSelectedItem(); + + if( vgaGain == null ) + { + vgaGain = DEFAULT_GAIN.getVGAGain(); + } + + if( mComboVGAGain.isEnabled() ) + { + mController.setVGAGain( vgaGain, true ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the VGA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply VGA " + + "gain setting", e ); + } + } + } ); + mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboVGAGain.setEnabled( false ); + gainPanel.add( new JLabel( "VGA" ) ); + gainPanel.add( mComboVGAGain, "wrap" ); + + add( gainPanel, "span" ); + } +} diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java b/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java deleted file mode 100644 index c761a07a..00000000 --- a/src/source/tuner/rtl/r820t/R820TTunerEditorPanel.java +++ /dev/null @@ -1,121 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.rtl.r820t; - -import gui.control.JFrequencyControl; - -import java.awt.Color; -import java.awt.EventQueue; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.IFrequencyChangeProcessor; -import source.tuner.rtl.RTL2832InfoPanel; - -import com.jidesoft.swing.JideTabbedPane; - -public class R820TTunerEditorPanel extends JPanel implements IFrequencyChangeProcessor -{ - private final static Logger mLog = - LoggerFactory.getLogger( R820TTunerEditorPanel.class ); - - private static final long serialVersionUID = 1L; - - private R820TTunerController mController; - private SettingsManager mSettingsManager; - - private JFrequencyControl mFrequencyControl; - - public R820TTunerEditorPanel( R820TTunerController controller, - SettingsManager settingsManager ) - { - mController = controller; - mSettingsManager = settingsManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow]" ) ); - - add( new JLabel( "RTL2832 with R820T Tuner" ), "span 2, align center, wrap" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as frequency change listener. */ - mController.addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span 2, align center, wrap" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.addTab( "Configuration", - new R820TTunerConfigurationPanel( mSettingsManager, mController ) ); - - tabs.addTab( "Info", new RTL2832InfoPanel( mController ) ); - - add( tabs, "grow,push,span" ); - } - - /** - * Frequency change listener for the frequency control gui component to - * apply end-user requested frequency changes against the tuner - */ - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - final long frequency = event.getValue().longValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequency( frequency ); - } - catch ( SourceException e ) - { - mLog.error( "R820T Tuner Controller - error setting frequency [" + - frequency + "]", e ); - } - } - } ); - } - } -} diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 0ef337c0..c3b27325 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -62,7 +62,7 @@ import settings.ColorSettingMenuItem; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerSelectionListener; +import source.tuner.TunerEvent; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; @@ -78,7 +78,6 @@ import spectrum.menu.SmoothingTypeItem; import com.jidesoft.swing.JideSplitPane; -import controller.ConfigurationControllerModel; import controller.channel.Channel; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; @@ -89,8 +88,7 @@ import dsp.filter.smoothing.SmoothingFilter.SmoothingType; public class SpectralDisplayPanel extends JPanel implements Listener, IFrequencyChangeProcessor, - IDFTWidthChangeProcessor, - TunerSelectionListener + IDFTWidthChangeProcessor { private static final long serialVersionUID = 1L; @@ -114,7 +112,6 @@ public class SpectralDisplayPanel extends JPanel private OverlayPanel mOverlayPanel; private DFTProcessor mDFTProcessor; private DFTResultsConverter mDFTConverter; - private ConfigurationControllerModel mControllerModel; private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; private SettingsManager mSettingsManager; @@ -132,12 +129,10 @@ public class SpectralDisplayPanel extends JPanel * the DFT is translated to decibels for display in the spectrum and * waterfall components. */ - public SpectralDisplayPanel( ConfigurationControllerModel controllerModel, - ChannelModel channelModel, + public SpectralDisplayPanel( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, SettingsManager settingsManager ) { - mControllerModel = controllerModel; mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; mSettingsManager = settingsManager; @@ -146,12 +141,6 @@ public class SpectralDisplayPanel extends JPanel mOverlayPanel = new OverlayPanel( mSettingsManager, mChannelModel ); mWaterfallPanel = new WaterfallPanel( mSettingsManager ); - //Register to receive tuner selection events - if( mControllerModel != null ) - { - mControllerModel.addListener( (TunerSelectionListener)this ); - } - init(); loadSettings(); @@ -190,12 +179,6 @@ public class SpectralDisplayPanel extends JPanel /* De-register from receiving samples when the window closes */ clearTuner(); - if( mControllerModel != null ) - { - mControllerModel.removeListener( (TunerSelectionListener)this ); - } - - mControllerModel = null; mSettingsManager = null; mDFTProcessor.dispose(); @@ -217,6 +200,11 @@ public class SpectralDisplayPanel extends JPanel mTuner = null; } + public Listener getTunerEventListener() + { + return new TunerSelectionListener( this ); + } + /** * Queues an FFT size change request. The scheduled executor will apply * the change when it runs. @@ -442,11 +430,10 @@ public class SpectralDisplayPanel extends JPanel } /** - * Responds to tuner selection event by deregistering from the current + * Responds to tuner event by deregistering from the current * complex sample buffer source and registering with the tuner argument. */ - @Override - public void tunerSelected( Tuner tuner ) + public void showTuner( Tuner tuner ) { clearTuner(); @@ -975,4 +962,26 @@ public class SpectralDisplayPanel extends JPanel } ); } } + + /** + * Provides tuner event listener interface for showing tuners in this display + */ + public class TunerSelectionListener implements Listener + { + private SpectralDisplayPanel mSpectralDisplayPanel; + + public TunerSelectionListener( SpectralDisplayPanel spectralDisplayPanel ) + { + mSpectralDisplayPanel = spectralDisplayPanel; + } + + @Override + public void receive( TunerEvent event ) + { + if( event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) + { + mSpectralDisplayPanel.showTuner( event.getTuner() ); + } + } + } } diff --git a/src/spectrum/SpectrumFrame.java b/src/spectrum/SpectrumFrame.java index a094be24..a3e1e5d1 100644 --- a/src/spectrum/SpectrumFrame.java +++ b/src/spectrum/SpectrumFrame.java @@ -26,7 +26,6 @@ import javax.swing.JFrame; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; import source.tuner.Tuner; -import controller.ConfigurationControllerModel; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; @@ -36,8 +35,7 @@ public class SpectrumFrame extends JFrame implements WindowListener private SpectralDisplayPanel mSpectralDisplayPanel; - public SpectrumFrame( ConfigurationControllerModel controllerModel, - ChannelModel channelModel, + public SpectrumFrame( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, SettingsManager settingsManager, Tuner tuner ) @@ -48,10 +46,10 @@ public class SpectrumFrame extends JFrame implements WindowListener setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); - mSpectralDisplayPanel = new SpectralDisplayPanel( controllerModel, - channelModel, channelProcessingManager, settingsManager ); - - mSpectralDisplayPanel.tunerSelected( tuner ); + mSpectralDisplayPanel = new SpectralDisplayPanel( channelModel, + channelProcessingManager, settingsManager ); + + mSpectralDisplayPanel.showTuner( tuner ); add( mSpectralDisplayPanel, "grow" ); /* Register a shutdown listener */ From 1078bdc4fde651699667d562403eaa3ec34d166c Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 12 Mar 2016 13:11:06 -0500 Subject: [PATCH 54/74] wip: #34 - adds tuner spectral display manager to handle requests to show a tuner in the main spectral display or to show the tuner in a new spectral display --- src/gui/SDRTrunk.java | 8 ++- .../tuner/TunerSpectralDisplayManager.java | 55 +++++++++++++++++++ src/spectrum/SpectralDisplayPanel.java | 28 ---------- 3 files changed, 61 insertions(+), 30 deletions(-) create mode 100644 src/source/tuner/TunerSpectralDisplayManager.java diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index dc84bb70..a1eb8c68 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -59,6 +59,7 @@ import settings.SettingsManager; import source.SourceManager; import source.tuner.TunerEvent; import source.tuner.TunerModel; +import source.tuner.TunerSpectralDisplayManager; import source.tuner.configuration.TunerConfigurationModel; import spectrum.SpectralDisplayPanel; import util.TimeStamp; @@ -162,8 +163,11 @@ public class SDRTrunk implements Listener mSpectralPanel = new SpectralDisplayPanel( channelModel, channelProcessingManager, mSettingsManager ); - - tunerModel.addListener( mSpectralPanel.getTunerEventListener() ); + + TunerSpectralDisplayManager tunerSpectralDisplayManager = + new TunerSpectralDisplayManager( mSpectralPanel, + channelModel, channelProcessingManager, mSettingsManager ); + tunerModel.addListener( tunerSpectralDisplayManager ); tunerModel.addListener( this ); PlaylistManager playlistManager = new PlaylistManager( threadPoolManager, diff --git a/src/source/tuner/TunerSpectralDisplayManager.java b/src/source/tuner/TunerSpectralDisplayManager.java new file mode 100644 index 00000000..dc6a5857 --- /dev/null +++ b/src/source/tuner/TunerSpectralDisplayManager.java @@ -0,0 +1,55 @@ +package source.tuner; + +import java.awt.EventQueue; + +import sample.Listener; +import settings.SettingsManager; +import spectrum.SpectralDisplayPanel; +import spectrum.SpectrumFrame; +import controller.channel.ChannelModel; +import controller.channel.ChannelProcessingManager; + +public class TunerSpectralDisplayManager implements Listener +{ + private SpectralDisplayPanel mSpectralDisplayPanel; + private ChannelModel mChannelModel; + private ChannelProcessingManager mChannelProcessingManager; + private SettingsManager mSettingsManager; + + public TunerSpectralDisplayManager( SpectralDisplayPanel panel, + ChannelModel channelModel, + ChannelProcessingManager channelProcessingManager, + SettingsManager settingsManager ) + { + mSpectralDisplayPanel = panel; + mChannelModel = channelModel; + mChannelProcessingManager = channelProcessingManager; + mSettingsManager = settingsManager; + } + + @Override + public void receive( TunerEvent event ) + { + switch( event.getEvent() ) + { + case REQUEST_MAIN_SPECTRAL_DISPLAY: + mSpectralDisplayPanel.showTuner( event.getTuner() ); + break; + case REQUEST_SECONDARY_SPECTRAL_DISPLAY: + final SpectrumFrame frame = new SpectrumFrame( mChannelModel, + mChannelProcessingManager, mSettingsManager, event.getTuner() ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + frame.setVisible( true ); + } + } ); + break; + default: + break; + } + } +} diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index c3b27325..1c095bcd 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -62,7 +62,6 @@ import settings.ColorSettingMenuItem; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerEvent; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; @@ -200,11 +199,6 @@ public class SpectralDisplayPanel extends JPanel mTuner = null; } - public Listener getTunerEventListener() - { - return new TunerSelectionListener( this ); - } - /** * Queues an FFT size change request. The scheduled executor will apply * the change when it runs. @@ -962,26 +956,4 @@ public class SpectralDisplayPanel extends JPanel } ); } } - - /** - * Provides tuner event listener interface for showing tuners in this display - */ - public class TunerSelectionListener implements Listener - { - private SpectralDisplayPanel mSpectralDisplayPanel; - - public TunerSelectionListener( SpectralDisplayPanel spectralDisplayPanel ) - { - mSpectralDisplayPanel = spectralDisplayPanel; - } - - @Override - public void receive( TunerEvent event ) - { - if( event.getEvent() == TunerEvent.Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) - { - mSpectralDisplayPanel.showTuner( event.getTuner() ); - } - } - } } From 415f95ec898f8d22496e0742f3792b6729547bb3 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 13 Mar 2016 07:09:26 -0400 Subject: [PATCH 55/74] wip #34: refactored tuners to take a tuner controller as an argument. Removed frequency change listener framework from tuner so that the tuner controller handles all frequency change interaction. Updated tuner view to allow selecting tuners for main spectral display and/or launching a new spectral display by clicking in the tuner table cell. --- src/gui/control/JFrequencyControl.java | 4 +- src/source/tuner/MixerTuner.java | 7 +- src/source/tuner/Tuner.java | 118 +++++------------- src/source/tuner/TunerChannelSource.java | 4 +- src/source/tuner/TunerController.java | 28 ++++- src/source/tuner/TunerEditor.java | 71 ++++++----- src/source/tuner/TunerEvent.java | 2 +- src/source/tuner/TunerModel.java | 27 ++-- .../tuner/TunerSpectralDisplayManager.java | 2 +- src/source/tuner/TunerViewPanel.java | 77 +++++++++++- src/source/tuner/airspy/AirspyTuner.java | 35 ++---- .../tuner/airspy/AirspyTunerEditorPanel.java | 2 +- src/source/tuner/ettus/B100Tuner.java | 2 +- src/source/tuner/fcd/FCDTuner.java | 35 +++--- .../tuner/fcd/proV1/FCD1TunerEditorPanel.java | 2 +- .../fcd/proplusV2/FCD2TunerEditorPanel.java | 2 +- .../tuner/frequency/FrequencyChangeEvent.java | 3 +- src/source/tuner/hackrf/HackRFTuner.java | 44 +++---- .../tuner/hackrf/HackRFTunerEditorPanel.java | 2 +- src/source/tuner/rtl/RTL2832Tuner.java | 40 +++--- src/spectrum/SpectralDisplayPanel.java | 4 +- 21 files changed, 263 insertions(+), 248 deletions(-) diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index 95b88249..cbc2c5b0 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -180,8 +180,8 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess Iterator it = mProcessors.iterator(); - FrequencyChangeEvent event = - new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, mFrequency ); + FrequencyChangeEvent event = new FrequencyChangeEvent( + Event.REQUEST_FREQUENCY_CHANGE, mFrequency ); while( it.hasNext() ) { diff --git a/src/source/tuner/MixerTuner.java b/src/source/tuner/MixerTuner.java index 42786ca8..37f79bed 100644 --- a/src/source/tuner/MixerTuner.java +++ b/src/source/tuner/MixerTuner.java @@ -46,11 +46,12 @@ public abstract class MixerTuner extends Tuner implements Listener)this ); } - public MixerTuner( String name, + public MixerTuner( String name, + TunerController tunerController, MixerTunerDataLine mixerTuner, ISampleAdapter sampleAdapter ) { this( name, + tunerController, mixerTuner.getMixerTunerType(), mixerTuner.getTargetDataLine(), sampleAdapter ); diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 0a0215f2..498d32f5 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -22,30 +22,25 @@ import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; -import settings.SettingsManager; import source.SourceException; import source.tuner.configuration.TunerConfiguration; import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; /** * Tuner - provides tuner channel sources, representing a channel frequency */ -public abstract class Tuner implements IFrequencyChangeProcessor, - ITunerChannelProvider +public abstract class Tuner implements ITunerChannelProvider { private final static Logger mLog = LoggerFactory.getLogger( Tuner.class ); private String mName; - + private TunerController mTunerController; /** * Sample Listeners - these will typically be the DFT processor for spectral * display, or it will be one or more tuner channel sources @@ -53,15 +48,40 @@ public abstract class Tuner implements IFrequencyChangeProcessor, protected List> mSampleListeners = new CopyOnWriteArrayList<>(); - protected List mFrequencyChangeProcessors = - new CopyOnWriteArrayList<>(); - protected List> mTunerChangeListeners = new ArrayList<>(); - public Tuner( String name ) + public Tuner( String name, TunerController tunerController ) { mName = name; + mTunerController = tunerController; + + //Rebroadcast frequency and sample rate change events as tuner events + mTunerController.addListener( new IFrequencyChangeProcessor() + { + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + switch( event.getEvent() ) + { + case NOTIFICATION_FREQUENCY_CHANGE: + broadcast( new TunerEvent( Tuner.this, + source.tuner.TunerEvent.Event.FREQUENCY )); + break; + case NOTIFICATION_SAMPLE_RATE_CHANGE: + broadcast( new TunerEvent( Tuner.this, + source.tuner.TunerEvent.Event.SAMPLE_RATE )); + break; + default: + break; + } + } + } ); + } + + public TunerController getTunerController() + { + return mTunerController; } public String toString() @@ -72,7 +92,6 @@ public abstract class Tuner implements IFrequencyChangeProcessor, public void dispose() { mSampleListeners.clear(); - mFrequencyChangeProcessors.clear(); } public void setName( String name ) @@ -189,32 +208,6 @@ public abstract class Tuner implements IFrequencyChangeProcessor, } } - /** - * Adds the frequency change listener to receive frequency and/or bandwidth - * change events - */ - public void addFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) - { - mFrequencyChangeProcessors.add( processor ); - } - - /** - * Removes the frequency change listener - */ - public void removeFrequencyChangeProcessor( IFrequencyChangeProcessor processor ) - { - mFrequencyChangeProcessors.remove( processor ); - } - - /** - * Broadcasts a frequency change event to all registered listeners - */ - public void broadcastFrequencyChange( long frequency ) - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.NOTIFICATION_FREQUENCY_CHANGE, frequency ) ); - } - /** * Registers the listener */ @@ -241,51 +234,4 @@ public abstract class Tuner implements IFrequencyChangeProcessor, listener.receive( event ); } } - - /** - * Broadcasts a sample rate change event to all registered listeners - */ - public void broadcastSampleRateChange( int sampleRate ) - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, sampleRate ) ); - } - - /** - * Broadcasts actual sample rate change event to all registered listeners - */ - public void broadcastActualSampleRateChange( int actualSampleRate ) - { - broadcastFrequencyChangeEvent( - new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, actualSampleRate ) ); - } - - /** - * Broadcasts a frequency change event to all registered listeners - */ - public void broadcastFrequencyChangeEvent( FrequencyChangeEvent event ) - { - for( IFrequencyChangeProcessor processor: mFrequencyChangeProcessors ) - { - processor.frequencyChanged( event ); - } - - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - broadcast( new TunerEvent( this, TunerEvent.Event.FREQUENCY ) ); - } - else if( event.getEvent() == Event.NOTIFICATION_SAMPLE_RATE_CHANGE ) - { - broadcast( new TunerEvent( this, TunerEvent.Event.SAMPLE_RATE ) ); - } - } - - /** - * Frequency change listener method. We receive change events from the - * controller and rebroadcast them to all registered listeners. - */ - public void frequencyChanged( FrequencyChangeEvent event ) - { - broadcastFrequencyChangeEvent( event ); - } } diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index fada5575..748a42fd 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -93,7 +93,7 @@ public class TunerChannelSource extends ComplexSource throws RejectedExecutionException, SourceException { mTuner = tuner; - mTuner.addListener( this ); + mTuner.getTunerController().addListener( (IFrequencyChangeProcessor)this ); mTunerChannel = tunerChannel; @@ -172,7 +172,7 @@ public class TunerChannelSource extends ComplexSource if( !mRunning.get() ) { /* Tell the tuner to release/unregister our resources */ - mTuner.removeFrequencyChangeProcessor( this ); + mTuner.getTunerController().removeListener( this ); mTuner = null; mTunerChannel = null; mBuffer = null; diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 832b4b2f..21061552 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -26,12 +26,13 @@ import org.slf4j.LoggerFactory; import source.SourceException; import source.tuner.configuration.TunerConfiguration; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.FrequencyController; import source.tuner.frequency.FrequencyController.Tunable; import source.tuner.frequency.IFrequencyChangeProcessor; -import controller.ThreadPoolManager; -public abstract class TunerController implements Tunable +public abstract class TunerController implements Tunable, IFrequencyChangeProcessor { private final static Logger mLog = LoggerFactory.getLogger( TunerController.class ); @@ -73,8 +74,27 @@ public abstract class TunerController implements Tunable */ public abstract void apply( TunerConfiguration config ) throws SourceException; - - public int getBandwidth() + + /** + * Responds to requests to set the frequency + */ + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + if( event.getEvent() == Event.REQUEST_FREQUENCY_CHANGE ) + { + try + { + setFrequency( event.getValue().longValue() ); + } + catch ( SourceException e ) + { + mLog.error( "Error setting frequency from external controller", e ); + } + } + } + + public int getBandwidth() { return mFrequencyController.getBandwidth(); } diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 18799074..0b7f9514 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -19,6 +19,7 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import source.SourceException; import source.tuner.configuration.TunerConfiguration; import source.tuner.configuration.TunerConfigurationModel; @@ -33,7 +34,6 @@ public class TunerEditor extends Editor private TunerConfigurationModel mTunerConfigurationModel; private JTable mTunerConfigurationTable; private TableRowSorter mRowSorter; - private ConfigurationRowFilter mRowFilter = new ConfigurationRowFilter(); private JFrequencyControl mFrequencyControl = new JFrequencyControl(); public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) @@ -51,14 +51,8 @@ public class TunerEditor extends Editor "[grow,fill]", "[][][grow,fill][]" ) ); listPanel.add( mFrequencyControl ); -// mFrequencyControl.addListener( this ); -// /* Add frequency control as frequency change listener. */ -// mController.addListener( mFrequencyControl ); -// mFrequencyControl.setFrequency( mController.getFrequency(), false ); - mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); mRowSorter = new TableRowSorter<>( mTunerConfigurationModel ); - mRowSorter.setRowFilter( mRowFilter ); mTunerConfigurationTable.setRowSorter( mRowSorter ); listPanel.add( new JLabel( "Tuner Configurations" ) ); @@ -95,16 +89,25 @@ public class TunerEditor extends Editor @Override public void save() { + //Unused } @Override public void setItem( Tuner tuner ) { + //Cleanup previously selected tuner + if( hasItem() ) + { + Tuner previous = getItem(); + previous.getTunerController().removeListener( mFrequencyControl ); + mFrequencyControl.removeListener( previous.getTunerController() ); + } + super.setItem( tuner ); if( hasItem() ) { - mRowFilter.setTunerType( tuner.getTunerType() ); + mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType() ) ); TunerConfiguration assigned = mTunerConfigurationModel .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); @@ -113,31 +116,43 @@ public class TunerEditor extends Editor if( modelIndex >= 0 ) { - int viewIndex = mTunerConfigurationTable.convertRowIndexToView( modelIndex ); + int viewIndex = mTunerConfigurationTable + .convertRowIndexToView( modelIndex ); if( viewIndex >= 0 ) { - mTunerConfigurationTable.setRowSelectionInterval( viewIndex, viewIndex ); + mTunerConfigurationTable + .setRowSelectionInterval( viewIndex, viewIndex ); } } + + //Link frequency control to the tuner + mFrequencyControl.addListener( tuner.getTunerController() ); + + //Link tuner to frequency control + tuner.getTunerController().addListener( mFrequencyControl ); + + //Set the displayed frequency without adjusting the tuner's frequency + try + { + mFrequencyControl.setFrequency( tuner.getFrequency(), false ); + } + catch( SourceException e ) + { + mLog.debug( "Couldn't get frequency from tuner: " + tuner.getUniqueID() ); + } } else { - mRowFilter.setTunerType( null ); + mRowSorter.setRowFilter( null ); } - - mRowSorter.setRowFilter( mRowFilter ); } public class ConfigurationRowFilter extends RowFilter { private TunerType mTunerType; - public ConfigurationRowFilter() - { - } - - public void setTunerType( TunerType type ) + public ConfigurationRowFilter( TunerType type ) { mTunerType = type; } @@ -146,22 +161,10 @@ public class TunerEditor extends Editor public boolean include( javax.swing.RowFilter.Entry entry ) { - if( mTunerType == null ) - { - return true; - } - else - { - TunerConfigurationModel model = entry.getModel(); - TunerConfiguration config = model.getTunerConfiguration( entry.getIdentifier() ); - - if( config != null ) - { - return config.getTunerType() == mTunerType; - } - } - - return false; + TunerConfiguration config = entry.getModel() + .getTunerConfiguration( entry.getIdentifier() ); + + return config != null && config.getTunerType() == mTunerType; } } diff --git a/src/source/tuner/TunerEvent.java b/src/source/tuner/TunerEvent.java index cccdc8fa..f34a1fd4 100644 --- a/src/source/tuner/TunerEvent.java +++ b/src/source/tuner/TunerEvent.java @@ -29,6 +29,6 @@ public class TunerEvent FREQUENCY, SAMPLE_RATE, REQUEST_MAIN_SPECTRAL_DISPLAY, - REQUEST_SECONDARY_SPECTRAL_DISPLAY; + REQUEST_NEW_SPECTRAL_DISPLAY; } } diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java index 14ca0a3d..a4505c61 100644 --- a/src/source/tuner/TunerModel.java +++ b/src/source/tuner/TunerModel.java @@ -6,6 +6,8 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.RejectedExecutionException; +import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.table.AbstractTableModel; import org.slf4j.Logger; @@ -25,16 +27,17 @@ public class TunerModel extends AbstractTableModel implements Listener mTuners = new ArrayList<>(); private List> mTunerEventListeners = new ArrayList<>(); @@ -183,7 +186,7 @@ public class TunerModel extends AbstractTableModel implements Listener case REQUEST_MAIN_SPECTRAL_DISPLAY: mSpectralDisplayPanel.showTuner( event.getTuner() ); break; - case REQUEST_SECONDARY_SPECTRAL_DISPLAY: + case REQUEST_NEW_SPECTRAL_DISPLAY: final SpectrumFrame frame = new SpectrumFrame( mChannelModel, mChannelProcessingManager, mSettingsManager, event.getTuner() ); diff --git a/src/source/tuner/TunerViewPanel.java b/src/source/tuner/TunerViewPanel.java index c6a5879d..3bedee68 100644 --- a/src/source/tuner/TunerViewPanel.java +++ b/src/source/tuner/TunerViewPanel.java @@ -1,19 +1,28 @@ package source.tuner; +import java.awt.Color; +import java.awt.Component; import java.awt.Dimension; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import source.tuner.TunerEvent.Event; + import com.jidesoft.swing.JideSplitPane; public class TunerViewPanel extends JPanel @@ -23,7 +32,6 @@ public class TunerViewPanel extends JPanel private TunerModel mTunerModel; private JTable mTunerTable; - private JideSplitPane mSplitPane; private TunerEditor mTunerEditor; @@ -55,6 +63,46 @@ public class TunerViewPanel extends JPanel } } } ); + mTunerTable.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( MouseEvent e ) + { + int column = mTunerTable.columnAtPoint( e.getPoint() ); + + if( column == TunerModel.SPECTRAL_DISPLAY_MAIN ) + { + int tableRow = mTunerTable.rowAtPoint( e.getPoint() ); + int modelRow = mTunerTable.convertRowIndexToModel( tableRow ); + + Tuner tuner = mTunerModel.getTuner( modelRow ); + + if( tuner != null ) + { + mTunerModel.broadcast( new TunerEvent( tuner, + Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) ); + } + } + else if( column == TunerModel.SPECTRAL_DISPLAY_NEW ) + { + int tableRow = mTunerTable.rowAtPoint( e.getPoint() ); + int modelRow = mTunerTable.convertRowIndexToModel( tableRow ); + + Tuner tuner = mTunerModel.getTuner( modelRow ); + + if( tuner != null ) + { + mTunerModel.broadcast( new TunerEvent( tuner, + Event.REQUEST_NEW_SPECTRAL_DISPLAY ) ); + } + } + } + } ); + + TableCellRenderer renderer = new LinkCellRenderer(); + + mTunerTable.getColumnModel().getColumn( 5 ).setCellRenderer( renderer ); + mTunerTable.getColumnModel().getColumn( 6 ).setCellRenderer( renderer ); JScrollPane listScroller = new JScrollPane( mTunerTable ); listScroller.setPreferredSize( new Dimension( 400, 20 ) ); @@ -70,5 +118,30 @@ public class TunerViewPanel extends JPanel add( mSplitPane ); } - + public class LinkCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent( JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column ) + { + JLabel label = (JLabel)super.getTableCellRendererComponent( table, + value, isSelected, hasFocus, row, column ); + + label.setForeground( Color.BLUE.brighter() ); + + if( column == TunerModel.SPECTRAL_DISPLAY_MAIN ) + { + label.setToolTipText( "Show this tuner in the main spectral display" ); + } + else if( column == TunerModel.SPECTRAL_DISPLAY_NEW ) + { + label.setToolTipText( "Show this tuner in a new spectral display" ); + } + + return label; + } + } } diff --git a/src/source/tuner/airspy/AirspyTuner.java b/src/source/tuner/airspy/AirspyTuner.java index 3d1406f3..01a52201 100644 --- a/src/source/tuner/airspy/AirspyTuner.java +++ b/src/source/tuner/airspy/AirspyTuner.java @@ -19,20 +19,17 @@ package source.tuner.airspy; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; -import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; +import source.tuner.TunerEvent; import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.BoardID; import source.tuner.configuration.TunerConfiguration; @@ -41,28 +38,20 @@ public class AirspyTuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger( AirspyTuner.class ); - AirspyTunerController mController; - public AirspyTuner( AirspyTunerController controller ) { - super( "Airspy" ); - - mController = controller; - - /* Register for frequency/sample rate changes so that we can rebroadcast - * to any registered listeners */ - mController.addListener( this ); + super( "Airspy", controller ); } public AirspyTunerController getController() { - return mController; + return (AirspyTunerController)getTunerController(); } @Override public void apply( TunerConfiguration config ) throws SourceException { - mController.apply( config ); + getController().apply( config ); } @Override @@ -70,7 +59,7 @@ public class AirspyTuner extends Tuner { try { - return mController.getDeviceInfo().getSerialNumber(); + return getController().getDeviceInfo().getSerialNumber(); } catch( Exception e ) { @@ -99,7 +88,7 @@ public class AirspyTuner extends Tuner try { - rate = mController.getCurrentSampleRate(); + rate = getController().getCurrentSampleRate(); } catch ( SourceException e ) { @@ -118,7 +107,7 @@ public class AirspyTuner extends Tuner @Override public long getFrequency() throws SourceException { - return mController.getFrequency(); + return getController().getFrequency(); } @Override @@ -132,7 +121,7 @@ public class AirspyTuner extends Tuner //Consider implementing a fractional resampler to get a correct 48 kHz //output sample rate - TunerChannelSource source = mController.getChannel( this, channel ); + TunerChannelSource source = getController().getChannel( this, channel ); if( source != null ) { @@ -147,7 +136,7 @@ public class AirspyTuner extends Tuner @Override public int getChannelCount() { - return mController.getChannelCount(); + return getController().getChannelCount(); } @Override @@ -159,19 +148,19 @@ public class AirspyTuner extends Tuner /* Tell the controller to release the channel and cleanup */ if( source != null ) { - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } @Override public void addListener( Listener listener ) { - mController.addListener( listener ); + getController().addListener( listener ); } @Override public void removeListener( Listener listener ) { - mController.removeListener( listener ); + getController().removeListener( listener ); } } diff --git a/src/source/tuner/airspy/AirspyTunerEditorPanel.java b/src/source/tuner/airspy/AirspyTunerEditorPanel.java index 7305fce4..3cc93d89 100644 --- a/src/source/tuner/airspy/AirspyTunerEditorPanel.java +++ b/src/source/tuner/airspy/AirspyTunerEditorPanel.java @@ -75,7 +75,7 @@ public class AirspyTunerEditorPanel extends JPanel /* Add frequency control as frequency change listener. This creates a * feedback loop, so the control does not rebroadcast the event */ - mTuner.addFrequencyChangeProcessor( mFrequencyControl ); + mTuner.getTunerController().addListener( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); diff --git a/src/source/tuner/ettus/B100Tuner.java b/src/source/tuner/ettus/B100Tuner.java index 40e4c6b8..b836722a 100644 --- a/src/source/tuner/ettus/B100Tuner.java +++ b/src/source/tuner/ettus/B100Tuner.java @@ -46,7 +46,7 @@ public class B100Tuner extends Tuner //TODO: this should extend a USBTuner class, so that we can push the //complex sample listener and the frequency change listener up to that class - super( "Ettus B100 with WBX Tuner" ); + super( "Ettus B100 with WBX Tuner", null ); mDevice = device; } diff --git a/src/source/tuner/fcd/FCDTuner.java b/src/source/tuner/fcd/FCDTuner.java index 9ce5087b..f051ce7f 100644 --- a/src/source/tuner/fcd/FCDTuner.java +++ b/src/source/tuner/fcd/FCDTuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,71 +19,64 @@ package source.tuner.fcd; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import sample.Listener; import sample.adapter.ShortAdapter; import sample.complex.ComplexBuffer; -import settings.SettingsManager; import source.SourceException; import source.tuner.MixerTuner; import source.tuner.MixerTunerDataLine; -import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; +import source.tuner.TunerEvent; import source.tuner.TunerType; import source.tuner.configuration.TunerConfiguration; public class FCDTuner extends MixerTuner { - private FCDTunerController mController; - public FCDTuner( MixerTunerDataLine mixerTDL, FCDTunerController controller ) { - super( controller.getConfiguration().toString(), + super( controller.getConfiguration().toString(), + controller, mixerTDL, new ShortAdapter() ); - - mController = controller; - mController.addListener( this ); } public void dispose() { //TODO: release the mixer tuner data line as well - mController.dispose(); + getController().dispose(); } public FCDTunerController getController() { - return mController; + return (FCDTunerController)getTunerController(); } @Override public TunerClass getTunerClass() { - return mController.getTunerClass(); + return getController().getTunerClass(); } @Override public TunerType getTunerType() { - return mController.getTunerType(); + return getController().getTunerType(); } @Override public String getUniqueID() { - return mController.getUSBAddress(); + return getController().getUSBAddress(); } @Override public void apply( TunerConfiguration config )throws SourceException { - mController.apply( config ); + getController().apply( config ); } @Override @@ -101,14 +94,14 @@ public class FCDTuner extends MixerTuner @Override public long getFrequency() throws SourceException { - return mController.getFrequency(); + return getController().getFrequency(); } @Override public TunerChannelSource getChannel( TunerChannel tunerChannel ) throws RejectedExecutionException, SourceException { - TunerChannelSource source = mController.getChannel( this, tunerChannel ); + TunerChannelSource source = getController().getChannel( this, tunerChannel ); if( source != null ) { @@ -121,7 +114,7 @@ public class FCDTuner extends MixerTuner @Override public int getChannelCount() { - return mController.getChannelCount(); + return getController().getChannelCount(); } /** @@ -138,7 +131,7 @@ public class FCDTuner extends MixerTuner /* Tell the controller to release the channel and cleanup */ /* This will release the channel as a frequency change listener */ - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } } diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java index 83512616..ff8321fd 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java @@ -73,7 +73,7 @@ public class FCD1TunerEditorPanel extends JPanel implements IFrequencyChangeProc /* Add frequency control as listener to tuner to get frequency changes * that are invoked elsewhere, to keep the control in sync with the * frequency */ - mTuner.addFrequencyChangeProcessor( mFrequencyControl ); + mTuner.getController().addListener( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java index 6f49b4dc..8bda26c0 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java @@ -72,7 +72,7 @@ public class FCD2TunerEditorPanel extends JPanel implements IFrequencyChangeProc /* Add frequency control as frequency change listener. This creates a * feedback loop, so the control does not rebroadcast the event */ - mTuner.addFrequencyChangeProcessor( mFrequencyControl ); + mTuner.getController().addListener( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); diff --git a/src/source/tuner/frequency/FrequencyChangeEvent.java b/src/source/tuner/frequency/FrequencyChangeEvent.java index afadd058..9ceb3685 100644 --- a/src/source/tuner/frequency/FrequencyChangeEvent.java +++ b/src/source/tuner/frequency/FrequencyChangeEvent.java @@ -8,7 +8,8 @@ public class FrequencyChangeEvent NOTIFICATION_FREQUENCY_CORRECTION_CHANGE, NOTIFICATION_CHANNEL_FREQUENCY_CORRECTION_CHANGE, NOTIFICATION_SAMPLE_RATE_CHANGE, - + + REQUEST_FREQUENCY_CHANGE, REQUEST_CHANNEL_FREQUENCY_CORRECTION_CHANGE; } diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java index a0a52613..6f00e4bc 100644 --- a/src/source/tuner/hackrf/HackRFTuner.java +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,20 +19,17 @@ package source.tuner.hackrf; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; import sample.complex.ComplexBuffer; -import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; +import source.tuner.TunerEvent; import source.tuner.TunerType; import source.tuner.configuration.TunerConfiguration; import source.tuner.hackrf.HackRFTunerController.BoardID; @@ -41,16 +38,14 @@ public class HackRFTuner extends Tuner { private final static Logger mLog = LoggerFactory.getLogger( HackRFTuner.class ); - private HackRFTunerController mController; - public HackRFTuner( HackRFTunerController controller ) throws SourceException { - super( "HackRF" ); - - mController = controller; - - /* Register for frequency/sample rate changes */ - mController.addListener( this ); + super( "HackRF", controller ); + } + + public HackRFTunerController getController() + { + return (HackRFTunerController)getTunerController(); } public void dispose() @@ -58,11 +53,6 @@ public class HackRFTuner extends Tuner //TODO: dispose of something here } - public HackRFTunerController getController() - { - return mController; - } - @Override public TunerClass getTunerClass() { @@ -78,13 +68,13 @@ public class HackRFTuner extends Tuner @Override public void apply( TunerConfiguration config ) throws SourceException { - mController.apply( config ); + getController().apply( config ); } @Override public int getSampleRate() { - return mController.getSampleRate(); + return getController().getSampleRate(); } @Override @@ -96,14 +86,14 @@ public class HackRFTuner extends Tuner @Override public long getFrequency() throws SourceException { - return mController.getFrequency(); + return getController().getFrequency(); } @Override public TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException { - TunerChannelSource source = mController.getChannel( this, channel ); + TunerChannelSource source = getController().getChannel( this, channel ); if( source != null ) { @@ -116,7 +106,7 @@ public class HackRFTuner extends Tuner @Override public int getChannelCount() { - return mController.getChannelCount(); + return getController().getChannelCount(); } @Override @@ -128,7 +118,7 @@ public class HackRFTuner extends Tuner /* Tell the controller to release the channel and cleanup */ if( source != null ) { - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } @@ -137,7 +127,7 @@ public class HackRFTuner extends Tuner { try { - return mController.getSerial().getSerialNumber(); + return getController().getSerial().getSerialNumber(); } catch( Exception e ) { @@ -150,12 +140,12 @@ public class HackRFTuner extends Tuner @Override public void addListener( Listener listener ) { - mController.addListener( listener ); + getController().addListener( listener ); } @Override public void removeListener( Listener listener ) { - mController.removeListener( listener ); + getController().removeListener( listener ); } } diff --git a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java index fe32f16a..c0666bb0 100644 --- a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java +++ b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java @@ -87,7 +87,7 @@ public class HackRFTunerEditorPanel extends JPanel implements IFrequencyChangePr /* Add frequency control as frequency change listener. This creates a * feedback loop, so the control does not rebroadcast the event */ - mTuner.addFrequencyChangeProcessor( mFrequencyControl ); + mTuner.getController().addListener( mFrequencyControl ); mFrequencyControl.setFrequency( mController.getFrequency(), false ); diff --git a/src/source/tuner/rtl/RTL2832Tuner.java b/src/source/tuner/rtl/RTL2832Tuner.java index 1cbbd6ce..ee994ba7 100644 --- a/src/source/tuner/rtl/RTL2832Tuner.java +++ b/src/source/tuner/rtl/RTL2832Tuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,26 +19,21 @@ package source.tuner.rtl; import java.util.concurrent.RejectedExecutionException; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; import sample.Listener; import sample.complex.ComplexBuffer; -import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; -import source.tuner.TunerEvent; import source.tuner.TunerChannel; import source.tuner.TunerChannelSource; import source.tuner.TunerClass; +import source.tuner.TunerEvent; import source.tuner.TunerType; import source.tuner.configuration.TunerConfiguration; -import source.tuner.frequency.IFrequencyChangeListener; import source.tuner.rtl.RTL2832TunerController.SampleRate; -import controller.ThreadPoolManager; public class RTL2832Tuner extends Tuner { @@ -46,7 +41,6 @@ public class RTL2832Tuner extends Tuner LoggerFactory.getLogger( RTL2832Tuner.class ); private TunerClass mTunerClass; - protected RTL2832TunerController mController; public RTL2832Tuner( TunerClass tunerClass, RTL2832TunerController controller ) @@ -54,11 +48,9 @@ public class RTL2832Tuner extends Tuner { super( tunerClass.getVendorDeviceLabel() + "/" + controller.getTunerType().getLabel() + " #" + - controller.getUniqueID() ); + controller.getUniqueID(), controller ); mTunerClass = tunerClass; - mController = controller; - mController.addListener( this ); } public void dispose() @@ -68,13 +60,13 @@ public class RTL2832Tuner extends Tuner public RTL2832TunerController getController() { - return mController; + return (RTL2832TunerController)getTunerController(); } @Override public String getUniqueID() { - return mController.getUniqueID(); + return getController().getUniqueID(); } @Override @@ -86,13 +78,13 @@ public class RTL2832Tuner extends Tuner @Override public TunerType getTunerType() { - return mController.getTunerType(); + return getController().getTunerType(); } @Override public void apply( TunerConfiguration config ) throws SourceException { - mController.apply( config ); + getController().apply( config ); } @Override @@ -100,7 +92,7 @@ public class RTL2832Tuner extends Tuner { try { - return mController.getCurrentSampleRate(); + return getController().getCurrentSampleRate(); } catch( SourceException e ) { @@ -114,7 +106,7 @@ public class RTL2832Tuner extends Tuner { try { - mController.setSampleRate( sampleRate ); + getController().setSampleRate( sampleRate ); } catch( LibUsbException e ) { @@ -134,12 +126,12 @@ public class RTL2832Tuner extends Tuner @Override public long getFrequency() throws SourceException { - return mController.getFrequency(); + return getController().getFrequency(); } public void setFrequency( int frequency ) throws SourceException { - mController.setFrequency( frequency ); + getController().setFrequency( frequency ); } @Override @@ -151,7 +143,7 @@ public class RTL2832Tuner extends Tuner try { - source = mController.getChannel( this, channel ); + source = getController().getChannel( this, channel ); if( source != null ) { @@ -169,7 +161,7 @@ public class RTL2832Tuner extends Tuner @Override public int getChannelCount() { - return mController.getChannelCount(); + return getController().getChannelCount(); } /** @@ -185,19 +177,19 @@ public class RTL2832Tuner extends Tuner /* Tell the controller to release the channel and cleanup */ if( source != null ) { - mController.releaseChannel( source ); + getController().releaseChannel( source ); } } @Override public void addListener( Listener listener ) { - mController.addListener( listener ); + getController().addListener( listener ); } @Override public void removeListener( Listener listener ) { - mController.removeListener( listener ); + getController().removeListener( listener ); } } diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 1c095bcd..8af9c603 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -438,7 +438,7 @@ public class SpectralDisplayPanel extends JPanel if( mTuner != null ) { //Register to receive frequency change events - mTuner.addFrequencyChangeProcessor( this ); + mTuner.getTunerController().addListener( this ); //Register the dft processor to receive samples from the tuner mTuner.addListener( (Listener)mDFTProcessor ); @@ -471,7 +471,7 @@ public class SpectralDisplayPanel extends JPanel if( mTuner != null ) { //Deregister for frequency change events from the tuner - mTuner.removeFrequencyChangeProcessor( this ); + mTuner.getTunerController().removeListener( this ); //Deregister the dft processor from receiving samples mTuner.removeListener( (Listener)mDFTProcessor ); From 0605760fcdd4caf028c0bb645897e5407b254947 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 13 Mar 2016 07:38:29 -0400 Subject: [PATCH 56/74] wip #34 - removed some wrapper methods from tuner since the tuner controller is now exposed (frequency, sample rate, channel count, etc) --- src/source/tuner/Tuner.java | 23 ------------ src/source/tuner/TunerChannelSource.java | 8 ++-- src/source/tuner/TunerController.java | 5 +++ src/source/tuner/TunerEditor.java | 10 +---- src/source/tuner/TunerModel.java | 8 ++-- src/source/tuner/airspy/AirspyTuner.java | 37 ------------------- .../tuner/airspy/AirspyTunerController.java | 2 +- src/source/tuner/ettus/B100Tuner.java | 25 ------------- src/source/tuner/fcd/FCDTuner.java | 24 ------------ src/source/tuner/fcd/FCDTunerController.java | 3 +- .../tuner/fcd/proV1/FCD1TunerController.java | 4 +- .../fcd/proplusV2/FCD2TunerController.java | 2 + src/source/tuner/hackrf/HackRFTuner.java | 24 ------------ src/source/tuner/rtl/RTL2832Tuner.java | 33 ----------------- .../tuner/rtl/RTL2832TunerController.java | 2 +- src/spectrum/SpectralDisplayPanel.java | 20 ++++------ 16 files changed, 32 insertions(+), 198 deletions(-) diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 498d32f5..911a1ca5 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -99,12 +99,6 @@ public abstract class Tuner implements ITunerChannelProvider mName = name; } - /** - * Applies the settings from a saved tuner configuration setting object - */ - public abstract void apply( TunerConfiguration config ) - throws SourceException; - /** * Unique identifier for this tuner, used to lookup a tuner configuration * from the settings manager. @@ -135,23 +129,11 @@ public abstract class Tuner implements ITunerChannelProvider return mName; } - /** - * Sample rate of complex data coming from the Tuner - * @return - */ - public abstract int getSampleRate(); - /** * Sample size in bits */ public abstract double getSampleSize(); - /** - * Tuner Local Oscillator Frequency - * @return - frequency in Hertz - */ - public abstract long getFrequency() throws SourceException; - /** * Returns a tuner frequency channel source, tuned to the correct frequency * @@ -166,11 +148,6 @@ public abstract class Tuner implements ITunerChannelProvider public abstract TunerChannelSource getChannel( TunerChannel channel ) throws RejectedExecutionException, SourceException; - /** - * Count of currently sourced channels (TunerChannel). - */ - public abstract int getChannelCount(); - /** * Releases the tuned channel resources * diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 748a42fd..66fba403 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -97,16 +97,18 @@ public class TunerChannelSource extends ComplexSource mTunerChannel = tunerChannel; - mTunerFrequency = mTuner.getFrequency(); + mTunerFrequency = mTuner.getTunerController().getFrequency(); /* Setup the frequency translator to the current source frequency */ long frequencyOffset = mTunerFrequency - mTunerChannel.getFrequency(); - mMixer = new Oscillator( frequencyOffset, mTuner.getSampleRate() ); + mMixer = new Oscillator( frequencyOffset, + mTuner.getTunerController().getSampleRate() ); /* Fire a sample rate change event to setup the decimation chain */ frequencyChanged( new FrequencyChangeEvent( - Event.NOTIFICATION_SAMPLE_RATE_CHANGE, mTuner.getSampleRate() ) ); + Event.NOTIFICATION_SAMPLE_RATE_CHANGE, + mTuner.getTunerController().getSampleRate() ) ); } diff --git a/src/source/tuner/TunerController.java b/src/source/tuner/TunerController.java index 21061552..b0c240aa 100644 --- a/src/source/tuner/TunerController.java +++ b/src/source/tuner/TunerController.java @@ -119,6 +119,11 @@ public abstract class TunerController implements Tunable, IFrequencyChangeProces { return mFrequencyController.getFrequency(); } + + public int getSampleRate() + { + return mFrequencyController.getBandwidth(); + } public double getFrequencyCorrection() { diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 0b7f9514..394e8f4e 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -133,14 +133,8 @@ public class TunerEditor extends Editor tuner.getTunerController().addListener( mFrequencyControl ); //Set the displayed frequency without adjusting the tuner's frequency - try - { - mFrequencyControl.setFrequency( tuner.getFrequency(), false ); - } - catch( SourceException e ) - { - mLog.debug( "Couldn't get frequency from tuner: " + tuner.getUniqueID() ); - } + mFrequencyControl.setFrequency( + tuner.getTunerController().getFrequency(), false ); } else { diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java index a4505c61..80086c94 100644 --- a/src/source/tuner/TunerModel.java +++ b/src/source/tuner/TunerModel.java @@ -96,7 +96,7 @@ public class TunerModel extends AbstractTableModel implements Listener Date: Sun, 13 Mar 2016 08:43:55 -0400 Subject: [PATCH 57/74] wip #34 - adds enable/disable to tuner editor components and frequency control --- src/gui/control/JFrequencyControl.java | 21 ++++++- src/source/tuner/TunerClass.java | 2 +- src/source/tuner/TunerEditor.java | 79 +++++++++++++++++++----- src/source/tuner/TunerModel.java | 10 +-- src/source/tuner/airspy/AirspyTuner.java | 5 +- src/source/tuner/rtl/RTL2832Tuner.java | 2 +- 6 files changed, 91 insertions(+), 28 deletions(-) diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index cbc2c5b0..8e3b9802 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -129,6 +129,17 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess revalidate(); } + + @Override + public void setEnabled( boolean enabled ) + { + super.setEnabled( enabled ); + + for( Digit digit: mDigits.values() ) + { + digit.setEnabled( enabled ); + } + } /** * Receives a frequency change event invoked by another control. We don't @@ -246,7 +257,10 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess public void increment( boolean fireChangeEvent ) { - set( mValue + 1, fireChangeEvent ); + if( isEnabled() ) + { + set( mValue + 1, fireChangeEvent ); + } } public void decrement() @@ -256,7 +270,10 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess public void decrement( boolean fireChangeEvent ) { - set( mValue - 1, fireChangeEvent ); + if( isEnabled() ) + { + set( mValue - 1, fireChangeEvent ); + } } /** diff --git a/src/source/tuner/TunerClass.java b/src/source/tuner/TunerClass.java index 8326cb2e..9b98be92 100644 --- a/src/source/tuner/TunerClass.java +++ b/src/source/tuner/TunerClass.java @@ -21,7 +21,7 @@ import javax.usb.UsbDeviceDescriptor; public enum TunerClass { - AIRSPY( TunerType.AIRSPY_R820T, "1D50", "60A1", "Airspy.com", "Airspy" ), + AIRSPY( TunerType.AIRSPY_R820T, "1D50", "60A1", "Airspy", "Airspy" ), GENERIC_2832( TunerType.RTL2832_VARIOUS, "0BDA", "2832", "RTL2832", "SDR" ), GENERIC_2838( TunerType.RTL2832_VARIOUS, "0BDA", "2838", "RTL2832", "SDR" ), COMPRO_VIDEOMATE_U620F( TunerType.ELONICS_E4000, "185B", "0620", "Compro", "Videomate U620F" ), diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 394e8f4e..3559a404 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -11,7 +11,10 @@ import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; +import javax.swing.JTextArea; import javax.swing.RowFilter; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; import javax.swing.table.TableRowSorter; import net.miginfocom.swing.MigLayout; @@ -35,6 +38,9 @@ public class TunerEditor extends Editor private JTable mTunerConfigurationTable; private TableRowSorter mRowSorter; private JFrequencyControl mFrequencyControl = new JFrequencyControl(); + private JLabel mSelectedTunerType = new JLabel( "No Tuner Selected" ); + private JButton mNewConfigurationButton = new JButton( "New" ); + private JButton mDeleteConfigurationButton = new JButton( "Delete" ); public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) { @@ -47,33 +53,53 @@ public class TunerEditor extends Editor setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]" ) ); JPanel listPanel = new JPanel(); - listPanel.setLayout( new MigLayout( "fill,wrap 1", - "[grow,fill]", "[][][grow,fill][]" ) ); - listPanel.add( mFrequencyControl ); + listPanel.setLayout( new MigLayout( "fill,wrap 2", "[grow,fill]", "[][][][grow,fill][]" ) ); + + listPanel.add( mSelectedTunerType, "span" ); + + mFrequencyControl.setEnabled( false ); + listPanel.add( mFrequencyControl, "span" ); mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); mRowSorter = new TableRowSorter<>( mTunerConfigurationModel ); mTunerConfigurationTable.setRowSorter( mRowSorter ); + mTunerConfigurationTable.setEnabled( false ); + mTunerConfigurationTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() + { + @Override + public void valueChanged( ListSelectionEvent event ) + { + if( !event.getValueIsAdjusting() ) + { + int row = mTunerConfigurationTable.getSelectedRow(); + + if( row >= 0 ) + { + int modelRow = mTunerConfigurationTable.convertRowIndexToModel( row ); - listPanel.add( new JLabel( "Tuner Configurations" ) ); - listPanel.add( mTunerConfigurationTable ); + if( modelRow >= 0 ) + { + setConfigurationEditor( mTunerConfigurationModel.getTunerConfiguration( modelRow ) ); + } + } + } + } + } ); - JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout( new MigLayout( "insets 0 0 0 0", - "[grow,fill][grow,fill]", "[grow,fill]" ) ); - - JButton newButton = new JButton( "New" ); - buttonPanel.add( newButton ); - - JButton deleteButton = new JButton( "Delete" ); - buttonPanel.add( deleteButton ); - - listPanel.add( buttonPanel ); + listPanel.add( new JLabel( "Tuner Configurations" ), "span" ); + listPanel.add( mTunerConfigurationTable, "span" ); + mNewConfigurationButton.setEnabled( false ); + listPanel.add( mNewConfigurationButton ); + + mDeleteConfigurationButton.setEnabled( false ); + listPanel.add( mDeleteConfigurationButton ); + //Set preferred size on both scrollers to same width, so they split the space JScrollPane listScroller = new JScrollPane( listPanel ); listScroller.setPreferredSize( new Dimension( 200, 80 ) ); +//TODO: this has to be changed JPanel editorPanel = new EmptyEditor(); JScrollPane editorScroller = new JScrollPane( editorPanel ); editorScroller.setPreferredSize( new Dimension( 200, 80 ) ); @@ -107,6 +133,11 @@ public class TunerEditor extends Editor if( hasItem() ) { + mSelectedTunerType.setText( tuner.getName() ); + mFrequencyControl.setEnabled( true ); + mTunerConfigurationTable.setEnabled( true ); + mNewConfigurationButton.setEnabled( true ); + mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType() ) ); TunerConfiguration assigned = mTunerConfigurationModel @@ -138,10 +169,26 @@ public class TunerEditor extends Editor } else { + mSelectedTunerType.setText( "No Tuner Selected" ); + mFrequencyControl.setEnabled( false ); + mTunerConfigurationTable.setEnabled( false ); + mNewConfigurationButton.setEnabled( false ); mRowSorter.setRowFilter( null ); } } + private void setConfigurationEditor( TunerConfiguration config ) + { + if( config != null ) + { + mDeleteConfigurationButton.setEnabled( true ); + } + else + { + mDeleteConfigurationButton.setEnabled( false ); + } + } + public class ConfigurationRowFilter extends RowFilter { private TunerType mTunerType; diff --git a/src/source/tuner/TunerModel.java b/src/source/tuner/TunerModel.java index 80086c94..3c386642 100644 --- a/src/source/tuner/TunerModel.java +++ b/src/source/tuner/TunerModel.java @@ -27,8 +27,8 @@ public class TunerModel extends AbstractTableModel implements Listener mTuners = new ArrayList<>(); private List> mTunerEventListeners = new ArrayList<>(); @@ -217,10 +217,10 @@ public class TunerModel extends AbstractTableModel implements Listener Date: Sun, 13 Mar 2016 20:06:26 -0400 Subject: [PATCH 58/74] wip: #34 --- src/source/tuner/TunerEditor.java | 45 +++++++++++++++++++ .../TunerConfigurationModel.java | 18 +++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 3559a404..c38f2d78 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -5,9 +5,12 @@ import gui.editor.Editor; import gui.editor.EmptyEditor; import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -24,6 +27,7 @@ import org.slf4j.LoggerFactory; import source.SourceException; import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationFactory; import source.tuner.configuration.TunerConfigurationModel; import com.jidesoft.swing.JideSplitPane; @@ -90,6 +94,47 @@ public class TunerEditor extends Editor listPanel.add( mTunerConfigurationTable, "span" ); mNewConfigurationButton.setEnabled( false ); + mNewConfigurationButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + Tuner tuner = getItem(); + + int counter = 0; + + String name = tuner.getUniqueID() + "-New"; + + while( counter < 10 && mTunerConfigurationModel + .hasTunerConfiguration( tuner.getTunerType(), name ) ) + { + counter++; + + StringBuilder sb = new StringBuilder(); + sb.append( tuner.getUniqueID() ); + sb.append( "-New(" ); + sb.append( String.valueOf( counter ) ); + sb.append( ")" ); + + name = sb.toString(); + } + + if( counter < 10 ) + { + TunerConfiguration config = TunerConfigurationFactory + .getTunerConfiguration( tuner.getTunerType(), name ); + + mTunerConfigurationModel.addTunerConfiguration( config ); + } + else + { + JOptionPane.showMessageDialog( TunerEditor.this, + "Can't create configuration - maximum limit reached", + "Error", + JOptionPane.OK_OPTION ); + } + } + } ); listPanel.add( mNewConfigurationButton ); mDeleteConfigurationButton.setEnabled( false ); diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index d41bd7c7..b047447c 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -202,7 +202,7 @@ public class TunerConfigurationModel extends AbstractTableModel if( configurationName == null ) { - configurationName = "Default-" + uniqueID; + configurationName = uniqueID; } List configs = getTunerConfigurations( type ); @@ -229,6 +229,22 @@ public class TunerConfigurationModel extends AbstractTableModel return config; } + + public boolean hasTunerConfiguration( TunerType type, String name ) + { + for( TunerConfiguration config: mTunerConfigurations ) + { + if( config.getTunerType() == type && + config.getName().contentEquals( name ) ) + { + mLog.debug( "Returning true for name: " + name ); + return true; + } + } + + mLog.debug( "Returning false for name: " + name ); + return false; + } /** * Assigns the tuner configuration to the tuner identified by the uniqueID From f518c15a06a8b97c4468084c07f6aed7f32b11a2 Mon Sep 17 00:00:00 2001 From: denny Date: Thu, 17 Mar 2016 21:16:21 -0400 Subject: [PATCH 59/74] wip #34 34: Redesign Channel Management Task-Url: http://github.com/DSheirer/sdrtrunk/issues/issue/34 --- src/source/tuner/TunerEditor.java | 52 ++++++--- .../TunerConfigurationEditor.java | 3 + .../TunerConfigurationFactory.java | 6 +- .../TunerConfigurationModel.java | 6 - .../tuner/rtl/r820t/R820TTunerEditor.java | 110 +++++++++--------- 5 files changed, 100 insertions(+), 77 deletions(-) diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index c38f2d78..a5e91e17 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -14,8 +14,8 @@ import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; -import javax.swing.JTextArea; import javax.swing.RowFilter; +import javax.swing.ScrollPaneConstants; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableRowSorter; @@ -25,8 +25,8 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import source.SourceException; import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; import source.tuner.configuration.TunerConfigurationFactory; import source.tuner.configuration.TunerConfigurationModel; @@ -45,6 +45,9 @@ public class TunerEditor extends Editor private JLabel mSelectedTunerType = new JLabel( "No Tuner Selected" ); private JButton mNewConfigurationButton = new JButton( "New" ); private JButton mDeleteConfigurationButton = new JButton( "Delete" ); + private JScrollPane mEditorScroller; + private Editor mEditor = new EmptyEditor<>(); + private JideSplitPane mEditorSplitPane = new JideSplitPane(); public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) { @@ -90,8 +93,10 @@ public class TunerEditor extends Editor } } ); + JScrollPane tableScroller = new JScrollPane( mTunerConfigurationTable ); + tableScroller.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); listPanel.add( new JLabel( "Tuner Configurations" ), "span" ); - listPanel.add( mTunerConfigurationTable, "span" ); + listPanel.add( tableScroller, "span" ); mNewConfigurationButton.setEnabled( false ); mNewConfigurationButton.addActionListener( new ActionListener() @@ -141,20 +146,18 @@ public class TunerEditor extends Editor listPanel.add( mDeleteConfigurationButton ); //Set preferred size on both scrollers to same width, so they split the space + listPanel.setPreferredSize( new Dimension( 100, 80 ) ); JScrollPane listScroller = new JScrollPane( listPanel ); - listScroller.setPreferredSize( new Dimension( 200, 80 ) ); + listScroller.setPreferredSize( new Dimension( 100, 80 ) ); -//TODO: this has to be changed - JPanel editorPanel = new EmptyEditor(); - JScrollPane editorScroller = new JScrollPane( editorPanel ); - editorScroller.setPreferredSize( new Dimension( 200, 80 ) ); + mEditorScroller = new JScrollPane( mEditor ); + mEditorScroller.setPreferredSize( new Dimension( 100, 80 ) ); - JideSplitPane splitPane = - new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); - splitPane.add( listScroller ); - splitPane.add( editorScroller ); + mEditorSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + mEditorSplitPane.add( listScroller ); + mEditorSplitPane.add( mEditorScroller ); - add( splitPane ); + add( mEditorSplitPane ); } @Override @@ -211,6 +214,9 @@ public class TunerEditor extends Editor //Set the displayed frequency without adjusting the tuner's frequency mFrequencyControl.setFrequency( tuner.getTunerController().getFrequency(), false ); + + mEditor = TunerConfigurationFactory + .getEditor( getItem(), mTunerConfigurationModel ); } else { @@ -219,19 +225,35 @@ public class TunerEditor extends Editor mTunerConfigurationTable.setEnabled( false ); mNewConfigurationButton.setEnabled( false ); mRowSorter.setRowFilter( null ); + mEditor = new EmptyEditor(); } + + //Swap out the editor + int split = mEditorSplitPane.getDividerLocation( 0 ); + mEditorSplitPane.remove( mEditorScroller ); + mEditorScroller = new JScrollPane( mEditor ); + mEditorSplitPane.add( mEditorScroller ); + mEditorSplitPane.setDividerLocation( 0, split ); } private void setConfigurationEditor( TunerConfiguration config ) { if( config != null ) { - mDeleteConfigurationButton.setEnabled( true ); + if( !mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( true ); + } } else { - mDeleteConfigurationButton.setEnabled( false ); + if( mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( false ); + } } + + mEditor.setItem( config ); } public class ConfigurationRowFilter extends RowFilter diff --git a/src/source/tuner/configuration/TunerConfigurationEditor.java b/src/source/tuner/configuration/TunerConfigurationEditor.java index 1c02f4b0..2c0908c5 100644 --- a/src/source/tuner/configuration/TunerConfigurationEditor.java +++ b/src/source/tuner/configuration/TunerConfigurationEditor.java @@ -1,6 +1,7 @@ package source.tuner.configuration; import gui.editor.Editor; +import source.tuner.TunerType; public abstract class TunerConfigurationEditor extends Editor { @@ -13,6 +14,8 @@ public abstract class TunerConfigurationEditor extends Editor getEditor( Tuner tuner, TunerConfigurationModel model ) { switch( tuner.getTunerType() ) @@ -58,7 +60,7 @@ public class TunerConfigurationFactory break; } - return null; + return new EmptyEditor(); } } diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index b047447c..36625241 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -3,16 +3,13 @@ package source.tuner.configuration; import java.util.ArrayList; import java.util.List; -import javax.swing.RowFilter; import javax.swing.table.AbstractTableModel; -import javax.swing.table.TableRowSorter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sample.Listener; import source.tuner.TunerType; -import source.tuner.TunerViewPanel; import source.tuner.configuration.TunerConfigurationEvent.Event; public class TunerConfigurationModel extends AbstractTableModel @@ -237,12 +234,10 @@ public class TunerConfigurationModel extends AbstractTableModel if( config.getTunerType() == type && config.getName().contentEquals( name ) ) { - mLog.debug( "Returning true for name: " + name ); return true; } } - mLog.debug( "Returning false for name: " + name ); return false; } @@ -382,5 +377,4 @@ public class TunerConfigurationModel extends AbstractTableModel return null; } - } diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index 0d106f8a..599ed4bd 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -29,6 +29,7 @@ import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerModel; @@ -45,6 +46,7 @@ import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; import source.SourceException; +import source.tuner.TunerType; import source.tuner.configuration.TunerConfiguration; import source.tuner.configuration.TunerConfigurationEditor; import source.tuner.configuration.TunerConfigurationEvent; @@ -83,8 +85,14 @@ public class R820TTunerEditor extends TunerConfigurationEditor init(); } - + @Override + public boolean canEdit( TunerType tunerType ) + { + return tunerType == TunerType.RAFAELMICRO_R820T; + } + + @Override public void save() { if( hasItem() ) @@ -147,9 +155,10 @@ public class R820TTunerEditor extends TunerConfigurationEditor private void init() { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][grow]" ) ); - add( new JLabel( "R820T Tuner" ), "span, align center" ); + add( new JLabel( "R820T Tuner Configuration" ), "span, align center" ); /** * Tuner Configuration Name @@ -166,9 +175,40 @@ public class R820TTunerEditor extends TunerConfigurationEditor public void focusGained( FocusEvent e ) {} } ); - add( new JLabel( "Configuration:" ) ); - add( mConfigurationName, "growx,push" ); + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span,grow" ); + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = + (SampleRate)mComboSampleRate.getSelectedItem(); + try + { + + mController.setSampleRate( sampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( + R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the sample " + + "rate setting [" + sampleRate.getLabel() + "] " + + eSampleRate.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply sample " + + "rate setting [" + sampleRate.getLabel() + "]", + eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); + /** * Frequency Correction */ @@ -208,50 +248,14 @@ public class R820TTunerEditor extends TunerConfigurationEditor } } ); - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); - /** - * Sample Rate - */ - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate, "growx,push" ); + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); /** * Gain Controls */ - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - /* Master Gain Control */ mComboMasterGain = new JComboBox( R820TGain.values() ); @@ -308,8 +312,8 @@ public class R820TTunerEditor extends TunerConfigurationEditor + "Mixer, LNA and Enhance gain
    " + "settings, or one of the individual gain settings for
    " + "semi-manual gain control" ); - gainPanel.add( new JLabel( "Master" ) ); - gainPanel.add( mComboMasterGain ); + add( new JLabel( "Master:" ) ); + add( mComboMasterGain ); /* Mixer Gain Control */ mComboMixerGain = new JComboBox( R820TMixerGain.values() ); @@ -356,8 +360,8 @@ public class R820TTunerEditor extends TunerConfigurationEditor mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + "to MASTER to enable adjustment" ); mComboMixerGain.setEnabled( false ); - gainPanel.add( new JLabel( "Mixer" ) ); - gainPanel.add( mComboMixerGain, "wrap" ); + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain ); /* LNA Gain Control */ mComboLNAGain = new JComboBox( R820TLNAGain.values() ); @@ -397,8 +401,8 @@ public class R820TTunerEditor extends TunerConfigurationEditor mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + "to MANUAL to enable adjustment" ); mComboLNAGain.setEnabled( false ); - gainPanel.add( new JLabel( "LNA" ) ); - gainPanel.add( mComboLNAGain ); + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); /* VGA Gain Control */ mComboVGAGain = new JComboBox( R820TVGAGain.values() ); @@ -438,9 +442,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " + "to MANUAL to enable adjustment" ); mComboVGAGain.setEnabled( false ); - gainPanel.add( new JLabel( "VGA" ) ); - gainPanel.add( mComboVGAGain, "wrap" ); - - add( gainPanel, "span" ); + add( new JLabel( "VGA:" ) ); + add( mComboVGAGain ); } } From 45f0e09d5ad92c334d7a76f59b7cd3a8e85321ec Mon Sep 17 00:00:00 2001 From: denny Date: Thu, 17 Mar 2016 22:01:31 -0400 Subject: [PATCH 60/74] wip #34 - updated tuner configurations to include unique id and assigned flag, so that we can get rid of tuner configuration assignment objects. --- src/settings/SettingsManager.java | 6 - src/source/tuner/TunerEditor.java | 55 ++---- .../airspy/AirspyTunerConfiguration.java | 5 +- .../configuration/TunerConfiguration.java | 42 +++- .../TunerConfigurationEvent.java | 21 +- .../TunerConfigurationFactory.java | 15 +- .../TunerConfigurationModel.java | 186 ++++++------------ .../fcd/proV1/FCD1TunerConfiguration.java | 5 +- .../fcd/proplusV2/FCD2TunerConfiguration.java | 5 +- .../hackrf/HackRFTunerConfiguration.java | 5 +- .../tuner/rtl/e4k/E4KTunerConfiguration.java | 5 +- .../rtl/r820t/R820TTunerConfiguration.java | 5 +- .../tuner/rtl/r820t/R820TTunerEditor.java | 2 +- 13 files changed, 139 insertions(+), 218 deletions(-) diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index 9529c9f6..4952ea9a 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -759,9 +759,6 @@ public class SettingsManager implements Listener mTunerConfigurationModel.addTunerConfigurations( mSettings.getTunerConfigurations() ); - - mTunerConfigurationModel.addTunerConfigurationAssignments( - mSettings.getSelectedTunerConfiguration() ); } } @@ -771,9 +768,6 @@ public class SettingsManager implements Listener { mSettings.setTunerConfigurations( mTunerConfigurationModel.getTunerConfigurations() ); - - mSettings.setSelectedTunerConfiguration( - mTunerConfigurationModel.getTunerConfigurationAssignments() ); } } diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index a5e91e17..9f310b33 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -10,7 +10,6 @@ import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -26,7 +25,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationEditor; import source.tuner.configuration.TunerConfigurationFactory; import source.tuner.configuration.TunerConfigurationModel; @@ -94,7 +92,8 @@ public class TunerEditor extends Editor } ); JScrollPane tableScroller = new JScrollPane( mTunerConfigurationTable ); - tableScroller.setHorizontalScrollBarPolicy( ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); + tableScroller.setHorizontalScrollBarPolicy( + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER ); listPanel.add( new JLabel( "Tuner Configurations" ), "span" ); listPanel.add( tableScroller, "span" ); @@ -106,38 +105,14 @@ public class TunerEditor extends Editor { Tuner tuner = getItem(); - int counter = 0; + String name = mTunerConfigurationModel + .getDistinctName( tuner.getTunerType(), tuner.getUniqueID() ); - String name = tuner.getUniqueID() + "-New"; - - while( counter < 10 && mTunerConfigurationModel - .hasTunerConfiguration( tuner.getTunerType(), name ) ) - { - counter++; + TunerConfiguration config = TunerConfigurationFactory + .getTunerConfiguration( tuner.getTunerType(), + tuner.getUniqueID(), name ); - StringBuilder sb = new StringBuilder(); - sb.append( tuner.getUniqueID() ); - sb.append( "-New(" ); - sb.append( String.valueOf( counter ) ); - sb.append( ")" ); - - name = sb.toString(); - } - - if( counter < 10 ) - { - TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration( tuner.getTunerType(), name ); - - mTunerConfigurationModel.addTunerConfiguration( config ); - } - else - { - JOptionPane.showMessageDialog( TunerEditor.this, - "Can't create configuration - maximum limit reached", - "Error", - JOptionPane.OK_OPTION ); - } + mTunerConfigurationModel.addTunerConfiguration( config ); } } ); listPanel.add( mNewConfigurationButton ); @@ -186,7 +161,8 @@ public class TunerEditor extends Editor mTunerConfigurationTable.setEnabled( true ); mNewConfigurationButton.setEnabled( true ); - mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType() ) ); + mRowSorter.setRowFilter( new ConfigurationRowFilter( + tuner.getTunerType(), tuner.getUniqueID() ) ); TunerConfiguration assigned = mTunerConfigurationModel .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); @@ -228,7 +204,7 @@ public class TunerEditor extends Editor mEditor = new EmptyEditor(); } - //Swap out the editor + //Swap out the editor int split = mEditorSplitPane.getDividerLocation( 0 ); mEditorSplitPane.remove( mEditorScroller ); mEditorScroller = new JScrollPane( mEditor ); @@ -259,10 +235,12 @@ public class TunerEditor extends Editor public class ConfigurationRowFilter extends RowFilter { private TunerType mTunerType; + private String mUniqueID; - public ConfigurationRowFilter( TunerType type ) + public ConfigurationRowFilter( TunerType type, String uniqueID ) { mTunerType = type; + mUniqueID = uniqueID; } @Override @@ -272,7 +250,10 @@ public class TunerEditor extends Editor TunerConfiguration config = entry.getModel() .getTunerConfiguration( entry.getIdentifier() ); - return config != null && config.getTunerType() == mTunerType; + return config != null && + config.getTunerType() == mTunerType && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( mUniqueID ); } } diff --git a/src/source/tuner/airspy/AirspyTunerConfiguration.java b/src/source/tuner/airspy/AirspyTunerConfiguration.java index 5b50c348..c9c47421 100644 --- a/src/source/tuner/airspy/AirspyTunerConfiguration.java +++ b/src/source/tuner/airspy/AirspyTunerConfiguration.java @@ -39,12 +39,11 @@ public class AirspyTunerConfiguration extends TunerConfiguration */ public AirspyTunerConfiguration() { - this( "Default" ); } - public AirspyTunerConfiguration( String name ) + public AirspyTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/configuration/TunerConfiguration.java b/src/source/tuner/configuration/TunerConfiguration.java index eee31f3f..040b829f 100644 --- a/src/source/tuner/configuration/TunerConfiguration.java +++ b/src/source/tuner/configuration/TunerConfiguration.java @@ -29,13 +29,23 @@ import source.tuner.TunerType; public abstract class TunerConfiguration { protected String mName; + protected String mUniqueID; + protected boolean mAssigned; /** * Default constructor to support JAXB */ public TunerConfiguration() { - this( "Unknown" ); + } + + /** + * Normal constructor + */ + public TunerConfiguration( String uniqueID, String name ) + { + mUniqueID = uniqueID; + mName = name; } public String toString() @@ -43,14 +53,6 @@ public abstract class TunerConfiguration return mName; } - /** - * Normal constructor - */ - public TunerConfiguration( String name ) - { - mName = name; - } - @XmlAttribute( name = "name" ) public String getName() { @@ -61,6 +63,28 @@ public abstract class TunerConfiguration { mName = name; } + + @XmlAttribute( name = "unique_id" ) + public String getUniqueID() + { + return mUniqueID; + } + + public void setUniqueID( String id ) + { + mUniqueID = id;; + } + + @XmlAttribute( name = "assigned" ) + public boolean isAssigned() + { + return mAssigned; + } + + public void setAssigned( boolean assigned ) + { + mAssigned = assigned; + } @XmlAttribute( name = "tuner_type" ) public abstract TunerType getTunerType(); diff --git a/src/source/tuner/configuration/TunerConfigurationEvent.java b/src/source/tuner/configuration/TunerConfigurationEvent.java index 3815c7b7..02a0769d 100644 --- a/src/source/tuner/configuration/TunerConfigurationEvent.java +++ b/src/source/tuner/configuration/TunerConfigurationEvent.java @@ -3,7 +3,6 @@ package source.tuner.configuration; public class TunerConfigurationEvent { private TunerConfiguration mTunerConfiguration; - private TunerConfigurationAssignment mTunerConfigurationAssignment; private Event mEvent; public TunerConfigurationEvent( TunerConfiguration configuration, Event event ) @@ -12,22 +11,11 @@ public class TunerConfigurationEvent mEvent = event; } - public TunerConfigurationEvent( TunerConfigurationAssignment assignment, Event event ) - { - mTunerConfigurationAssignment = assignment; - mEvent = event; - } - public TunerConfiguration getConfiguration() { return mTunerConfiguration; } - public TunerConfigurationAssignment getAssignment() - { - return mTunerConfigurationAssignment; - } - public Event getEvent() { return mEvent; @@ -35,11 +23,8 @@ public class TunerConfigurationEvent public enum Event { - CONFIGURATION_ADD, - CONFIGURATION_REMOVE, - CONFIGURATION_CHANGE, - ASSIGNMENT_ADD, - ASSIGNMENT_REMOVE, - ASSIGNMENT_CHANGE; + ADD, + REMOVE, + CHANGE; } } diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java index 35e774a5..8a93b54e 100644 --- a/src/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -15,22 +15,23 @@ import source.tuner.rtl.r820t.R820TTunerEditor; public class TunerConfigurationFactory { - public static TunerConfiguration getTunerConfiguration( TunerType type, String name ) + public static TunerConfiguration getTunerConfiguration( TunerType type, + String uniqueID, String name ) { switch( type ) { case AIRSPY_R820T: - return new AirspyTunerConfiguration( name ); + return new AirspyTunerConfiguration( uniqueID, name ); case ELONICS_E4000: - return new E4KTunerConfiguration( name ); + return new E4KTunerConfiguration( uniqueID, name ); case FUNCUBE_DONGLE_PRO: - return new FCD1TunerConfiguration( name ); + return new FCD1TunerConfiguration( uniqueID, name ); case FUNCUBE_DONGLE_PRO_PLUS: - return new FCD2TunerConfiguration( name ); + return new FCD2TunerConfiguration( uniqueID, name ); case HACKRF: - return new HackRFTunerConfiguration( name ); + return new HackRFTunerConfiguration( uniqueID, name ); case RAFAELMICRO_R820T: - return new R820TTunerConfiguration( name ); + return new R820TTunerConfiguration( uniqueID, name ); default: throw new IllegalArgumentException( "Unrecognized tuner type [" + type.name() + "] - can't create named [" + name + "] tuner" diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index 36625241..64bd45f3 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -20,11 +20,11 @@ public class TunerConfigurationModel extends AbstractTableModel private static final int TUNER_TYPE = 0; private static final int CONFIGURATION_NAME = 1; - private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name" }; + private static final int ASSIGNED = 2; + private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name", "Assigned" }; private List> mConfigurationListeners = new ArrayList<>(); private List mTunerConfigurations = new ArrayList<>(); - private List mTunerConfigurationAssignments = new ArrayList<>(); /** * Table model for managing tuner configurations and tuner configuration @@ -59,7 +59,9 @@ public class TunerConfigurationModel extends AbstractTableModel */ public void addTunerConfiguration( TunerConfiguration config ) { - if( !mTunerConfigurations.contains( config ) ) + if( !mTunerConfigurations.contains( config ) && + config.getUniqueID() != null && + config.getName() != null ) { mTunerConfigurations.add( config ); @@ -67,7 +69,7 @@ public class TunerConfigurationModel extends AbstractTableModel fireTableRowsInserted( index, index ); - broadcast( new TunerConfigurationEvent( config, Event.CONFIGURATION_ADD ) ); + broadcast( new TunerConfigurationEvent( config, Event.ADD ) ); } } @@ -85,7 +87,7 @@ public class TunerConfigurationModel extends AbstractTableModel fireTableRowsDeleted( index, index ); - broadcast( new TunerConfigurationEvent( config, Event.CONFIGURATION_REMOVE ) ); + broadcast( new TunerConfigurationEvent( config, Event.REMOVE ) ); } } @@ -98,7 +100,7 @@ public class TunerConfigurationModel extends AbstractTableModel { for( TunerConfiguration config: mTunerConfigurations ) { - broadcast( new TunerConfigurationEvent( config, Event.CONFIGURATION_REMOVE ) ); + broadcast( new TunerConfigurationEvent( config, Event.REMOVE ) ); } int size = mTunerConfigurations.size(); @@ -106,13 +108,6 @@ public class TunerConfigurationModel extends AbstractTableModel mTunerConfigurations.clear(); fireTableRowsDeleted( 0, size - 1 ); - - for( TunerConfigurationAssignment assignment: mTunerConfigurationAssignments ) - { - broadcast( new TunerConfigurationEvent( assignment, Event.ASSIGNMENT_REMOVE ) ); - } - - mTunerConfigurationAssignments.clear(); } /** @@ -142,96 +137,61 @@ public class TunerConfigurationModel extends AbstractTableModel } } - /** - * Adds a list of tuner configuration assignments to the model - */ - public void addTunerConfigurationAssignments( List assignments ) - { - for( TunerConfigurationAssignment assigment: assignments ) - { - addTunerConfigurationAssignment( assigment ); - } - } - - /** - * Returns of list of tuner configurations from the model - */ - public List getTunerConfigurationAssignments() - { - return mTunerConfigurationAssignments; - } - - /** - * Adds a tuner configuration assigment to the model - */ - public void addTunerConfigurationAssignment( TunerConfigurationAssignment assignment ) - { - mTunerConfigurationAssignments.add( assignment ); - - broadcast( new TunerConfigurationEvent( assignment, Event.ASSIGNMENT_ADD ) ); - } - - /** - * Removes a tuner configuration assignment from the model - */ - public void removeTunerConfigurationAssignment( TunerConfigurationAssignment assignment ) - { - mTunerConfigurationAssignments.remove( assignment ); - - broadcast( new TunerConfigurationEvent( assignment, Event.ASSIGNMENT_REMOVE ) ); - } - /** * Returns the tuner configuration that is assigned to the tuner with the - * tuner type and uniqueID, or creates a new tuner configuration and tuner - * configuration assignment for the tuner. + * tuner type and uniqueID, or selects the first existing configuration that + * matches the tuner type and unique ID and assigns it, or creates a new + * tuner configuration and assigns it to the tuner. + * + * This method will always return a tuner configuration that is assigned to + * the tuner type and unique ID. */ public TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID ) { - String configurationName = null; - - TunerConfigurationAssignment assignment = getTunerConfigurationAssignment( type, uniqueID ); - - if( assignment != null ) - { - configurationName = assignment.getTunerConfigurationName(); - } - - if( configurationName == null ) - { - configurationName = uniqueID; - } - - List configs = getTunerConfigurations( type ); + List configs = getTunerConfigurations( type, uniqueID ); for( TunerConfiguration config: configs ) { - if( config.getName().contentEquals( configurationName ) ) + if( config.isAssigned() && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( uniqueID ) ) { - if( assignment == null ) - { - assignTunerConfiguration( config, configurationName ); - } - return config; } } + //Reuse an existing configuration to assign + if( !configs.isEmpty() ) + { + configs.get( 0 ).setAssigned( true ); + return configs.get( 0 ); + } + + String name = getDistinctName( type, uniqueID ); + //We didn't find the config so create a new one and assign it TunerConfiguration config = TunerConfigurationFactory - .getTunerConfiguration( type, configurationName ); + .getTunerConfiguration( type, uniqueID, name ); + + config.setAssigned( true ); addTunerConfiguration( config ); - assignTunerConfiguration( config, configurationName ); return config; } - public boolean hasTunerConfiguration( TunerType type, String name ) + /** + * Indicates if a configuration exists in this model matching the tuner type + * and unique ID and name. + */ + public boolean hasTunerConfiguration( TunerType type, String uniqueID, String name ) { for( TunerConfiguration config: mTunerConfigurations ) { if( config.getTunerType() == type && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( uniqueID ) && + config.getName() != null && config.getName().contentEquals( name ) ) { return true; @@ -240,59 +200,39 @@ public class TunerConfigurationModel extends AbstractTableModel return false; } - - /** - * Assigns the tuner configuration to the tuner identified by the uniqueID - */ - public void assignTunerConfiguration( TunerConfiguration config, String uniqueID ) - { - TunerConfigurationAssignment assignment = - getTunerConfigurationAssignment( config.getTunerType(), uniqueID ); - - Event event = Event.ASSIGNMENT_CHANGE; - - if( assignment == null ) - { - assignment = new TunerConfigurationAssignment(); - assignment.setUniqueID( uniqueID ); - - addTunerConfigurationAssignment( assignment ); - - event = Event.ASSIGNMENT_ADD; - } - - assignment.setTunerConfigurationName( config.getName() ); - - broadcast( new TunerConfigurationEvent( assignment, event ) ); - } - - /** - * Returns the tuner configuration assiged to the tuner type and unique id - */ - private TunerConfigurationAssignment getTunerConfigurationAssignment( TunerType type, String uniqueID ) - { - for( TunerConfigurationAssignment assignment: mTunerConfigurationAssignments ) - { - if( assignment.getTunerType() == type && - assignment.getUniqueID().contentEquals( uniqueID ) ) - { - return assignment; - } - } - - return null; - } + /** + * Creates a configuration name for the tuner configuration that is distinct + * among the existing tuner configurations of the same type and uniqueID + */ + public String getDistinctName( TunerType type, String uniqueID ) + { + int counter = 0; + + String name = "New"; + + while( hasTunerConfiguration( type, uniqueID, name ) ) + { + counter++; + + name = "New (" + counter + ")"; + } + + return name; + } + /** * Returns all tuner configurations for the specified tuner type */ - public List getTunerConfigurations( TunerType type ) + public List getTunerConfigurations( TunerType type, String uniqueID ) { List configs = new ArrayList<>(); for( TunerConfiguration config: mTunerConfigurations ) { - if( config.getTunerType() == type ) + if( config.getTunerType() == type && + config.getUniqueID() != null && + config.getUniqueID().contentEquals( uniqueID ) ) { configs.add( config ); } @@ -370,6 +310,8 @@ public class TunerConfigurationModel extends AbstractTableModel return config.getTunerType().getLabel(); case CONFIGURATION_NAME: return config.getName(); + case ASSIGNED: + return config.isAssigned() ? "*" : null; default: break; } diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java index 43152221..9c474376 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java @@ -42,12 +42,11 @@ public class FCD1TunerConfiguration extends TunerConfiguration */ public FCD1TunerConfiguration() { - this( "Default" ); } - public FCD1TunerConfiguration( String name ) + public FCD1TunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java index b2955570..0f001b13 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerConfiguration.java @@ -33,12 +33,11 @@ public class FCD2TunerConfiguration extends TunerConfiguration */ public FCD2TunerConfiguration() { - this( "Default" ); } - public FCD2TunerConfiguration( String name ) + public FCD2TunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/hackrf/HackRFTunerConfiguration.java b/src/source/tuner/hackrf/HackRFTunerConfiguration.java index 33356fc0..14cd6a66 100644 --- a/src/source/tuner/hackrf/HackRFTunerConfiguration.java +++ b/src/source/tuner/hackrf/HackRFTunerConfiguration.java @@ -39,12 +39,11 @@ public class HackRFTunerConfiguration extends TunerConfiguration */ public HackRFTunerConfiguration() { - this( "Default" ); } - public HackRFTunerConfiguration( String name ) + public HackRFTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java index 5c699d4d..072d2fa6 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java @@ -41,12 +41,11 @@ public class E4KTunerConfiguration extends TunerConfiguration */ public E4KTunerConfiguration() { - this( "Default" ); } - public E4KTunerConfiguration( String name ) + public E4KTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java index 95686f66..f1d92e1a 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfiguration.java @@ -41,12 +41,11 @@ public class R820TTunerConfiguration extends TunerConfiguration */ public R820TTunerConfiguration() { - this( "Default" ); } - public R820TTunerConfiguration( String name ) + public R820TTunerConfiguration( String uniqueID, String name ) { - super( name ); + super( uniqueID, name ); } @Override diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index 599ed4bd..32d9ab1f 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -120,7 +120,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor config.setVGAGain( vgaGain ); getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), Event.CONFIGURATION_CHANGE ) ); + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); } } From a4d69ce0aa024a75b4e8072322b42054aa1f8880 Mon Sep 17 00:00:00 2001 From: denny Date: Fri, 18 Mar 2016 10:59:36 -0400 Subject: [PATCH 61/74] wip #34 - final edits to R820T tuner configuration editor and introduced frequency correction to the airspy tuner configuration. --- src/gui/SDRTrunk.java | 4 +- src/gui/editor/EmptyEditor.java | 9 +- src/settings/Settings.java | 91 +---------- src/settings/SettingsManager.java | 77 ++++++--- src/source/recording/Recording.java | 6 +- src/source/tuner/TunerEditor.java | 96 +++++++++-- .../airspy/AirspyTunerConfiguration.java | 13 ++ .../airspy/AirspyTunerConfigurationPanel.java | 18 +-- .../TunerConfigurationAssignment.java | 66 -------- .../TunerConfigurationFactory.java | 3 +- .../TunerConfigurationModel.java | 45 +++++- .../proV1/FCD1TunerConfigurationPanel.java | 2 +- .../FCD2TunerConfigurationPanel.java | 6 +- .../hackrf/HackRFTunerConfigurationPanel.java | 20 +-- .../rtl/e4k/E4KTunerConfigurationPanel.java | 6 +- .../R820TTunerConfigurationEditorOld.java | 6 +- .../tuner/rtl/r820t/R820TTunerEditor.java | 151 ++++++++++++------ 17 files changed, 333 insertions(+), 286 deletions(-) delete mode 100644 src/source/tuner/configuration/TunerConfigurationAssignment.java diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index a1eb8c68..83f9f7ee 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -119,10 +119,10 @@ public class SDRTrunk implements Listener TunerConfigurationModel tunerConfigurationModel = new TunerConfigurationModel(); TunerModel tunerModel = new TunerModel( tunerConfigurationModel ); - mSettingsManager = new SettingsManager( tunerConfigurationModel ); - ThreadPoolManager threadPoolManager = new ThreadPoolManager(); + mSettingsManager = new SettingsManager( threadPoolManager, tunerConfigurationModel ); + AliasModel aliasModel = new AliasModel(); ChannelModel channelModel = new ChannelModel(); diff --git a/src/gui/editor/EmptyEditor.java b/src/gui/editor/EmptyEditor.java index 128407fc..7b16ee35 100644 --- a/src/gui/editor/EmptyEditor.java +++ b/src/gui/editor/EmptyEditor.java @@ -8,10 +8,15 @@ public class EmptyEditor extends Editor { private static final long serialVersionUID = 1L; - public EmptyEditor() + public EmptyEditor( String descriptor ) { setLayout( new MigLayout( "insets 0 0 0 0", "[center]", "[grow]" ) ); - add( new JLabel( "Please select an item" ), "wrap" ); + add( new JLabel( "Please select " + descriptor ), "wrap" ); + } + + public EmptyEditor() + { + this( "an item" ); } @Override diff --git a/src/settings/Settings.java b/src/settings/Settings.java index 1166592a..a7f2d261 100644 --- a/src/settings/Settings.java +++ b/src/settings/Settings.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -28,10 +28,8 @@ import map.DefaultIcon; import map.MapIcon; import settings.ColorSetting.ColorSettingName; import source.recording.RecordingConfiguration; -import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerConfiguration; import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; import source.tuner.hackrf.HackRFTunerConfiguration; @@ -54,8 +52,7 @@ import source.tuner.rtl.r820t.R820TTunerConfiguration; R820TTunerConfiguration.class, RecordingConfiguration.class, Setting.class, - TunerConfiguration.class, - TunerConfigurationAssignment.class} ) + TunerConfiguration.class } ) @XmlRootElement( name = "SDRTrunk_settings" ) public class Settings @@ -65,9 +62,6 @@ public class Settings private List mTunerConfiguration = new ArrayList(); - private List mConfigurationAssignments = - new ArrayList(); - private List mRecordingConfigurations = new ArrayList(); @@ -89,7 +83,6 @@ public class Settings public void addRecordingConfiguration( RecordingConfiguration config ) { mRecordingConfigurations.add( config ); - } public void removeRecordingConfiguration( RecordingConfiguration config ) @@ -107,33 +100,6 @@ public class Settings { mTunerConfiguration = configs; } - - public void addTunerConfiguration( TunerConfiguration config ) - { - mTunerConfiguration.add( config ); - - } - - public void removeTunerConfiguration( TunerConfiguration config ) - { - mTunerConfiguration.remove( config ); - } - - public ArrayList getTunerConfigurations( TunerType type ) - { - ArrayList configs = - new ArrayList(); - - for( TunerConfiguration config: mTunerConfiguration ) - { - if( config.getTunerType() == type ) - { - configs.add( config ); - } - } - - return configs; - } @XmlElement( name="setting" ) public List getSettings() @@ -254,59 +220,6 @@ public class Settings return icons; } - @XmlElement( name="tuner_configuration_assignment" ) - public List getSelectedTunerConfiguration() - { - return mConfigurationAssignments; - } - - public void setSelectedTunerConfiguration( List configs ) - { - mConfigurationAssignments = configs; - } - - public void addSelectedTunerConfiguration( TunerConfigurationAssignment config ) - { - mConfigurationAssignments.add( config ); - - } - - public void removeSelectedTunerConfiguration( TunerConfigurationAssignment config ) - { - mConfigurationAssignments.remove( config ); - } - - public TunerConfigurationAssignment getConfigurationAssignment( TunerType type, String uniqueID ) - { - for( TunerConfigurationAssignment config: mConfigurationAssignments ) - { - if( config.getTunerType() == type && - uniqueID.contentEquals( config.getUniqueID() ) ) - { - return config; - } - } - - return null; - } - - public void setConfigurationAssignment( TunerType type, - String uniqueID, - String tunerConfigurationName ) - { - TunerConfigurationAssignment config = getConfigurationAssignment( type, uniqueID ); - - if( config == null ) - { - config = new TunerConfigurationAssignment(); - config.setTunerType( type ); - config.setUniqueID( uniqueID ); - addSelectedTunerConfiguration( config ); - } - - config.setTunerConfigurationName( tunerConfigurationName ); - } - public MapViewSetting getMapViewSetting( String name ) { for( Setting setting: mSettings ) diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index 4952ea9a..cc5432b5 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -31,6 +31,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.ImageIcon; import javax.xml.bind.JAXBContext; @@ -45,6 +47,8 @@ import org.jdesktop.swingx.mapviewer.GeoPosition; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import controller.ThreadPoolManager; +import playlist.PlaylistManager.PlaylistSaveTask; import properties.SystemProperties; import sample.Listener; import settings.ColorSetting.ColorSettingName; @@ -72,14 +76,19 @@ public class SettingsManager implements Listener new HashMap(); private TunerConfigurationModel mTunerConfigurationModel; - private boolean mLoading = false; + private ThreadPoolManager mThreadPoolManager; + + private boolean mLoadingSettings = false; + private AtomicBoolean mSettingsSavePending = new AtomicBoolean(); - public SettingsManager( TunerConfigurationModel tunerConfigurationModel ) + public SettingsManager( ThreadPoolManager threadPoolManager, + TunerConfigurationModel tunerConfigurationModel ) { //TODO: move settings and icons into a SettingsModel and an IconModel //and update this class to only provide loading, saving, and model //change detection producing a save. - + + mThreadPoolManager = threadPoolManager; mTunerConfigurationModel = tunerConfigurationModel; //Register for tuner config events so that we can save the settings @@ -112,9 +121,9 @@ public class SettingsManager implements Listener @Override public void receive( TunerConfigurationEvent t ) { - if( !mLoading ) + if( !mLoadingSettings ) { - save(); + scheduleSettingsSave(); } } @@ -196,7 +205,7 @@ public class SettingsManager implements Listener broadcastSettingChange( setting ); - save(); + scheduleSettingsSave(); } public void resetColorSetting( ColorSettingName name ) @@ -242,7 +251,7 @@ public class SettingsManager implements Listener broadcastSettingChange( setting ); - save(); + scheduleSettingsSave(); } public MapIcon[] getMapIcons() @@ -339,7 +348,7 @@ public class SettingsManager implements Listener broadcastSettingDeleted( existing ); - save(); + scheduleSettingsSave(); } } @@ -362,7 +371,7 @@ public class SettingsManager implements Listener existing.setName( icon.getName() ); existing.setPath( icon.getPath() ); broadcastSettingChange( existing ); - save(); + scheduleSettingsSave(); } } @@ -379,7 +388,7 @@ public class SettingsManager implements Listener existing.setName( newName ); existing.setPath( newPath ); broadcastSettingChange( existing ); - save(); + scheduleSettingsSave(); } } @@ -539,7 +548,7 @@ public class SettingsManager implements Listener defaultIconSetting.setName( icon.getName() ); - save(); + scheduleSettingsSave(); broadcastSettingChange( defaultIconSetting ); } @@ -553,7 +562,7 @@ public class SettingsManager implements Listener { mSettings.addSetting( setting ); - save(); + scheduleSettingsSave(); broadcastSettingChange( setting ); } @@ -566,13 +575,13 @@ public class SettingsManager implements Listener public void addRecordingConfiguration( RecordingConfiguration config ) { mSettings.addRecordingConfiguration( config ); - save(); + scheduleSettingsSave(); } public void removeRecordingConfiguration( RecordingConfiguration config ) { mSettings.removeRecordingConfiguration( config ); - save(); + scheduleSettingsSave(); } public MapViewSetting getMapViewSetting( String name, GeoPosition position, int zoom ) @@ -600,11 +609,12 @@ public class SettingsManager implements Listener loc.setGeoPosition( position ); loc.setZoom( zoom ); - save(); + scheduleSettingsSave(); } - public void save() + private void save() { + mLog.debug( "Saving settings" ); saveTunerConfigurationModel(); JAXBContext context = null; @@ -685,7 +695,7 @@ public class SettingsManager implements Listener */ public void load( Path settingsPath ) { - mLoading = true; + mLoadingSettings = true; if( Files.exists( settingsPath ) ) { @@ -748,7 +758,7 @@ public class SettingsManager implements Listener loadTunerConfigurationModel(); - mLoading = false; + mLoadingSettings = false; } private void loadTunerConfigurationModel() @@ -818,4 +828,35 @@ public class SettingsManager implements Listener { mListeners.remove( listener ); } + + /** + * Schedules a settings save task. Subsequent calls to this method will be ignored until the + * save event occurs, thus limiting repetitive saving to a minimum. + */ + private void scheduleSettingsSave() + { + if( !mLoadingSettings ) + { + if( mSettingsSavePending.compareAndSet( false, true ) ) + { + mThreadPoolManager.scheduleOnce( new SettingsSaveTask(), + 2, TimeUnit.SECONDS ); + } + } + } + + /** + * Resets the settings save pending flag to false and proceeds to save the + * settings. + */ + public class SettingsSaveTask implements Runnable + { + @Override + public void run() + { + mSettingsSavePending.set( false ); + + save(); + } + } } diff --git a/src/source/recording/Recording.java b/src/source/recording/Recording.java index bfbe790e..f06192c2 100644 --- a/src/source/recording/Recording.java +++ b/src/source/recording/Recording.java @@ -38,7 +38,7 @@ public class Recording implements Comparable, public void setAlias( String alias ) { mConfiguration.setAlias( alias ); - mSettingsManager.save(); +// mSettingsManager.save(); } public void setRecordingFile( File file ) throws SourceException @@ -51,7 +51,7 @@ public class Recording implements Comparable, } mConfiguration.setFilePath( file.getAbsolutePath() ); - mSettingsManager.save(); +// mSettingsManager.save(); } /** @@ -96,7 +96,7 @@ public class Recording implements Comparable, mConfiguration.setCenterFrequency( frequency ); - mSettingsManager.save(); +// mSettingsManager.save(); mCenterFrequency = frequency; diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 9f310b33..6275174b 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -10,6 +10,7 @@ import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JLabel; +import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; @@ -43,8 +44,9 @@ public class TunerEditor extends Editor private JLabel mSelectedTunerType = new JLabel( "No Tuner Selected" ); private JButton mNewConfigurationButton = new JButton( "New" ); private JButton mDeleteConfigurationButton = new JButton( "Delete" ); + private JButton mAssignConfigurationButton = new JButton( "Assign" ); private JScrollPane mEditorScroller; - private Editor mEditor = new EmptyEditor<>(); + private Editor mEditor = new EmptyEditor<>( "a tuner" ); private JideSplitPane mEditorSplitPane = new JideSplitPane(); public TunerEditor( TunerConfigurationModel tunerConfigurationModel ) @@ -58,7 +60,7 @@ public class TunerEditor extends Editor setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[grow,fill]" ) ); JPanel listPanel = new JPanel(); - listPanel.setLayout( new MigLayout( "fill,wrap 2", "[grow,fill]", "[][][][grow,fill][]" ) ); + listPanel.setLayout( new MigLayout( "fill,wrap 3", "[grow,fill]", "[][][][grow,fill][]" ) ); listPanel.add( mSelectedTunerType, "span" ); @@ -84,7 +86,7 @@ public class TunerEditor extends Editor if( modelRow >= 0 ) { - setConfigurationEditor( mTunerConfigurationModel.getTunerConfiguration( modelRow ) ); + setTunerConfiguration( mTunerConfigurationModel.getTunerConfiguration( modelRow ) ); } } } @@ -118,8 +120,39 @@ public class TunerEditor extends Editor listPanel.add( mNewConfigurationButton ); mDeleteConfigurationButton.setEnabled( false ); + mDeleteConfigurationButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + int choice = JOptionPane.showConfirmDialog( TunerEditor.this, + "Do you want to delete this tuner configuration?", + "Delete Tuner Configuration?", JOptionPane.YES_NO_OPTION ); + + if( choice == JOptionPane.YES_OPTION ) + { + TunerConfiguration selected = getSelectedTunerConfiguration(); + + mTunerConfigurationModel.removeTunerConfiguration( selected ); + } + } + } ); listPanel.add( mDeleteConfigurationButton ); + mAssignConfigurationButton.setEnabled( false ); + mAssignConfigurationButton.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + TunerConfiguration selected = getSelectedTunerConfiguration(); + + mTunerConfigurationModel.assignTunerConfiguration( selected ); + setTunerConfiguration( getSelectedTunerConfiguration() ); + } + } ); + listPanel.add( mAssignConfigurationButton ); + //Set preferred size on both scrollers to same width, so they split the space listPanel.setPreferredSize( new Dimension( 100, 80 ) ); JScrollPane listScroller = new JScrollPane( listPanel ); @@ -135,6 +168,23 @@ public class TunerEditor extends Editor add( mEditorSplitPane ); } + private TunerConfiguration getSelectedTunerConfiguration() + { + int tableRow = mTunerConfigurationTable.getSelectedRow(); + + if( tableRow >= 0 ) + { + int modelRow = mTunerConfigurationTable.convertRowIndexToModel( tableRow ); + + if( modelRow >= 0 ) + { + return mTunerConfigurationModel.getTunerConfiguration( modelRow ); + } + } + + return null; + } + @Override public void save() { @@ -161,8 +211,11 @@ public class TunerEditor extends Editor mTunerConfigurationTable.setEnabled( true ); mNewConfigurationButton.setEnabled( true ); - mRowSorter.setRowFilter( new ConfigurationRowFilter( - tuner.getTunerType(), tuner.getUniqueID() ) ); + //Change to an editor for this config's tuner type + mEditor = TunerConfigurationFactory.getEditor( getItem(), mTunerConfigurationModel ); + + mRowSorter.setRowFilter( new ConfigurationRowFilter( tuner.getTunerType(), + tuner.getUniqueID() ) ); TunerConfiguration assigned = mTunerConfigurationModel .getTunerConfiguration( tuner.getTunerType(), tuner.getUniqueID() ); @@ -190,9 +243,6 @@ public class TunerEditor extends Editor //Set the displayed frequency without adjusting the tuner's frequency mFrequencyControl.setFrequency( tuner.getTunerController().getFrequency(), false ); - - mEditor = TunerConfigurationFactory - .getEditor( getItem(), mTunerConfigurationModel ); } else { @@ -212,13 +262,32 @@ public class TunerEditor extends Editor mEditorSplitPane.setDividerLocation( 0, split ); } - private void setConfigurationEditor( TunerConfiguration config ) + private void setTunerConfiguration( TunerConfiguration config ) { if( config != null ) { - if( !mDeleteConfigurationButton.isEnabled() ) + if( !config.isAssigned() ) { - mDeleteConfigurationButton.setEnabled( true ); + if( !mAssignConfigurationButton.isEnabled() ) + { + mAssignConfigurationButton.setEnabled( true ); + } + if( !mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( true ); + } + } + else + { + if( mAssignConfigurationButton.isEnabled() ) + { + mAssignConfigurationButton.setEnabled( false ); + } + + if( mDeleteConfigurationButton.isEnabled() ) + { + mDeleteConfigurationButton.setEnabled( false ); + } } } else @@ -227,6 +296,11 @@ public class TunerEditor extends Editor { mDeleteConfigurationButton.setEnabled( false ); } + + if( mAssignConfigurationButton.isEnabled() ) + { + mAssignConfigurationButton.setEnabled( false ); + } } mEditor.setItem( config ); diff --git a/src/source/tuner/airspy/AirspyTunerConfiguration.java b/src/source/tuner/airspy/AirspyTunerConfiguration.java index c9c47421..9a62bdbe 100644 --- a/src/source/tuner/airspy/AirspyTunerConfiguration.java +++ b/src/source/tuner/airspy/AirspyTunerConfiguration.java @@ -30,6 +30,7 @@ public class AirspyTunerConfiguration extends TunerConfiguration private int mIFGain = AirspyTunerController.IF_GAIN_DEFAULT; private int mMixerGain = AirspyTunerController.MIXER_GAIN_DEFAULT; private int mLNAGain = AirspyTunerController.LNA_GAIN_DEFAULT; + private double mFrequencyCorrection = 0.0d; private boolean mMixerAGC = false; private boolean mLNAAGC = false; @@ -128,4 +129,16 @@ public class AirspyTunerConfiguration extends TunerConfiguration { mLNAAGC = enabled; } + + @XmlAttribute( name = "frequency_correction" ) + public double getFrequencyCorrection() + { + return mFrequencyCorrection; + } + + public void setFrequencyCorrection( double value ) + { + mFrequencyCorrection = value; + } + } diff --git a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java index 781c52e2..60617e57 100644 --- a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java +++ b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java @@ -21,7 +21,6 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; -import java.util.ArrayList; import java.util.List; import javax.swing.ComboBoxModel; @@ -47,11 +46,8 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerType; import source.tuner.airspy.AirspyTunerController.Gain; import source.tuner.airspy.AirspyTunerController.GainMode; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationAssignment; public class AirspyTunerConfigurationPanel extends JPanel { @@ -119,7 +115,7 @@ public class AirspyTunerConfigurationPanel extends JPanel mComboConfigurations.setModel( getModel() ); /* Determine which tuner configuration should be selected/displayed */ - TunerConfigurationAssignment savedConfig = null; +// TunerConfigurationAssignment savedConfig = null; String serial = null; serial = mController.getDeviceInfo().getSerialNumber(); @@ -130,11 +126,11 @@ public class AirspyTunerConfigurationPanel extends JPanel // TunerType.AIRSPY_R820T, serial ); } - if( savedConfig != null ) - { - mSelectedConfig = getNamedConfiguration( - savedConfig.getTunerConfigurationName() ); - } +// if( savedConfig != null ) +// { +// mSelectedConfig = getNamedConfiguration( +// savedConfig.getTunerConfigurationName() ); +// } /* If we couldn't determine the saved/selected config, use the first one */ if( mSelectedConfig == null ) @@ -694,6 +690,6 @@ public class AirspyTunerConfigurationPanel extends JPanel */ private void save() { - mSettingsManager.save(); +// mSettingsManager.save(); } } diff --git a/src/source/tuner/configuration/TunerConfigurationAssignment.java b/src/source/tuner/configuration/TunerConfigurationAssignment.java deleted file mode 100644 index 3e3fcf2e..00000000 --- a/src/source/tuner/configuration/TunerConfigurationAssignment.java +++ /dev/null @@ -1,66 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.configuration; - -import javax.xml.bind.annotation.XmlAttribute; - -import source.tuner.TunerType; - -public class TunerConfigurationAssignment -{ - private TunerType mTunerType; - private String mUniqueID; - private String mTunerConfigurationName; - - public TunerConfigurationAssignment() - { - } - - @XmlAttribute( name = "type" ) - public TunerType getTunerType() - { - return mTunerType; - } - - public void setTunerType( TunerType tunerType ) - { - mTunerType = tunerType; - } - - @XmlAttribute( name = "id" ) - public String getUniqueID() - { - return mUniqueID; - } - - public void setUniqueID( String uniqueID ) - { - mUniqueID = uniqueID; - } - - @XmlAttribute( name = "configuration_name" ) - public String getTunerConfigurationName() - { - return mTunerConfigurationName; - } - - public void setTunerConfigurationName( String tunerConfigurationName ) - { - mTunerConfigurationName = tunerConfigurationName; - } -} diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java index 8a93b54e..d41fc726 100644 --- a/src/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -61,7 +61,6 @@ public class TunerConfigurationFactory break; } - return new EmptyEditor(); + return new EmptyEditor( "a tuner" ); } - } diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index 64bd45f3..44f4008f 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -23,12 +23,13 @@ public class TunerConfigurationModel extends AbstractTableModel private static final int ASSIGNED = 2; private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name", "Assigned" }; - private List> mConfigurationListeners = new ArrayList<>(); + private List> mConfigurationChangeListeners = + new ArrayList<>(); private List mTunerConfigurations = new ArrayList<>(); /** - * Table model for managing tuner configurations and tuner configuration - * assignments. Provides tuner configuration events to registered listeners + * Table model for managing tuner configurations. Provides tuner configuration events to + * registered listeners */ public TunerConfigurationModel() { @@ -115,7 +116,7 @@ public class TunerConfigurationModel extends AbstractTableModel */ public void addListener( Listener listener ) { - mConfigurationListeners.add( listener ); + mConfigurationChangeListeners.add( listener ); } /** @@ -123,7 +124,7 @@ public class TunerConfigurationModel extends AbstractTableModel */ public void removeListener( Listener listener ) { - mConfigurationListeners.remove( listener ); + mConfigurationChangeListeners.remove( listener ); } /** @@ -131,12 +132,44 @@ public class TunerConfigurationModel extends AbstractTableModel */ public void broadcast( TunerConfigurationEvent event ) { - for( Listener listener: mConfigurationListeners ) + for( Listener listener: mConfigurationChangeListeners ) { listener.receive( event ); } } + /** + * Designates the configuration argument as the currently assigned configuration and unassigns + * any other configurations with the same tuner type and unique ID. + */ + public void assignTunerConfiguration( TunerConfiguration configToAssign ) + { + if( configToAssign != null ) + { + List configs = + getTunerConfigurations( configToAssign.getTunerType(), configToAssign.getUniqueID() ); + + for( TunerConfiguration config: configs ) + { + if( config.isAssigned() ) + { + mLog.debug( "Unassigning: " + config.getName() ); + config.setAssigned( false ); + + int index = mTunerConfigurations.indexOf( config ); + fireTableCellUpdated( index, ASSIGNED ); + broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); + } + } + + mLog.debug( "Assigning: " + configToAssign.getName() ); + configToAssign.setAssigned( true ); + int index = mTunerConfigurations.indexOf( configToAssign ); + fireTableCellUpdated( index, ASSIGNED ); + broadcast( new TunerConfigurationEvent( configToAssign, Event.CHANGE ) ); + } + } + /** * Returns the tuner configuration that is assigned to the tuner with the * tuner type and uniqueID, or selects the first existing configuration that diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java index 051d597f..390f3f21 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java @@ -557,7 +557,7 @@ public class FCD1TunerConfigurationPanel extends JPanel private void save() { - mSettingsManager.save(); +// mSettingsManager.save(); } public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java index 991d0b11..cdcaff7e 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java @@ -23,7 +23,6 @@ import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DecimalFormat; -import java.util.ArrayList; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; @@ -48,9 +47,6 @@ import org.slf4j.LoggerFactory; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationAssignment; public class FCD2TunerConfigurationPanel extends JPanel { @@ -375,6 +371,6 @@ public class FCD2TunerConfigurationPanel extends JPanel private void save() { - mSettingsManager.save(); +// mSettingsManager.save(); } } diff --git a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java index 88aa95d6..92ff881b 100644 --- a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java +++ b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java @@ -23,7 +23,6 @@ import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DecimalFormat; -import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.ComboBoxModel; @@ -50,9 +49,6 @@ import org.slf4j.LoggerFactory; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; @@ -105,7 +101,7 @@ public class HackRFTunerConfigurationPanel extends JPanel mComboConfigurations.setModel( getModel() ); /* Determine which tuner configuration should be selected/displayed */ - TunerConfigurationAssignment savedConfig = null; +// TunerConfigurationAssignment savedConfig = null; String serial = null; try @@ -122,12 +118,12 @@ public class HackRFTunerConfigurationPanel extends JPanel // savedConfig = mSettingsManager.getSelectedTunerConfiguration( // TunerType.HACKRF, serial ); // } - - if( savedConfig != null ) - { - mSelectedConfig = getNamedConfiguration( - savedConfig.getTunerConfigurationName() ); - } +// +// if( savedConfig != null ) +// { +// mSelectedConfig = getNamedConfiguration( +// savedConfig.getTunerConfigurationName() ); +// } /* If we couldn't determine the saved/selected config, use the first one */ if( mSelectedConfig == null ) @@ -556,6 +552,6 @@ public class HackRFTunerConfigurationPanel extends JPanel */ private void save() { - mSettingsManager.save(); +// mSettingsManager.save(); } } diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java index b411ff5e..3ec3e0db 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java @@ -23,7 +23,6 @@ import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DecimalFormat; -import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.ComboBoxModel; @@ -50,9 +49,6 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; import source.tuner.rtl.e4k.E4KTunerController.E4KGain; @@ -686,6 +682,6 @@ public class E4KTunerConfigurationPanel extends JPanel */ private void save() { - mSettingsManager.save(); +// mSettingsManager.save(); } } diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java index 1a310ba1..841a8e49 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java +++ b/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java @@ -23,7 +23,6 @@ import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DecimalFormat; -import java.util.ArrayList; import javax.swing.BorderFactory; import javax.swing.ComboBoxModel; @@ -50,9 +49,6 @@ import org.usb4java.LibUsbException; import settings.SettingsManager; import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.configuration.TunerConfigurationAssignment; import source.tuner.rtl.RTL2832TunerController.SampleRate; import source.tuner.rtl.r820t.R820TTunerController.R820TGain; import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; @@ -694,6 +690,6 @@ public class R820TTunerConfigurationEditorOld extends JPanel */ private void save() { - mSettingsManager.save(); +// mSettingsManager.save(); } } diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index 32d9ab1f..4fa0a41c 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -17,18 +17,15 @@ ******************************************************************************/ package source.tuner.rtl.r820t; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DecimalFormat; -import javax.swing.BorderFactory; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.JTextField; @@ -76,6 +73,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor private JComboBox mComboLNAGain; private JComboBox mComboVGAGain; private JComboBox mComboSampleRate; + private boolean mLoading; public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) @@ -95,7 +93,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor @Override public void save() { - if( hasItem() ) + if( hasItem() && !mLoading ) { R820TTunerConfiguration config = getConfiguration(); @@ -133,14 +131,73 @@ public class R820TTunerEditor extends TunerConfigurationEditor { R820TTunerConfiguration config = getConfiguration(); - mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mComboMasterGain.setSelectedItem( config.getMasterGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboVGAGain.setSelectedItem( config.getVGAGain() ); + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( item.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + + mLoading = false; } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + } + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboVGAGain.isEnabled() != enabled ) + { + mComboVGAGain.setEnabled( enabled ); + } } private R820TTunerConfiguration getConfiguration() @@ -164,6 +221,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor * Tuner Configuration Name */ mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); mConfigurationName.addFocusListener( new FocusListener() { @Override @@ -179,6 +237,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor add( mConfigurationName, "span,grow" ); mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.setEnabled( false ); mComboSampleRate.addActionListener( new ActionListener() { @Override @@ -219,6 +278,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor 0.1 ); //step mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); @@ -258,7 +318,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor */ /* Master Gain Control */ mComboMasterGain = new JComboBox( R820TGain.values() ); - + mComboMasterGain.setEnabled( false ); mComboMasterGain.addActionListener( new ActionListener() { @Override @@ -292,7 +352,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor mComboVGAGain.setEnabled( false ); mComboVGAGain.setSelectedItem( gain.getVGAGain() ); } - + save(); } catch ( UsbException e ) @@ -317,54 +377,49 @@ public class R820TTunerEditor extends TunerConfigurationEditor /* Mixer Gain Control */ mComboMixerGain = new JComboBox( R820TMixerGain.values() ); + mComboMixerGain.setEnabled( false ); mComboMixerGain.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent arg0 ) { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - R820TMixerGain mixerGain = - (R820TMixerGain)mComboMixerGain.getSelectedItem(); - - if( mixerGain == null ) - { - mixerGain = DEFAULT_GAIN.getMixerGain(); - } - - if( mComboMixerGain.isEnabled() ) - { - mController.setMixerGain( mixerGain, true ); - } + try + { + R820TMixerGain mixerGain = + (R820TMixerGain)mComboMixerGain.getSelectedItem(); + + if( mixerGain == null ) + { + mixerGain = DEFAULT_GAIN.getMixerGain(); + } + + if( mComboMixerGain.isEnabled() ) + { + mController.setMixerGain( mixerGain, true ); + } - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( R820TTunerEditor.this, - "R820T Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply mixer " - + "gain setting - ", e ); - } - } - } ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, + "R820T Tuner Controller - couldn't apply the mixer " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "R820T Tuner Controller - couldn't apply mixer " + + "gain setting - ", e ); + } } } ); mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); + add( new JLabel( "Mixer:" ) ); add( mComboMixerGain ); /* LNA Gain Control */ mComboLNAGain = new JComboBox( R820TLNAGain.values() ); + mComboLNAGain.setEnabled( false ); mComboLNAGain.addActionListener( new ActionListener() { @Override @@ -400,12 +455,13 @@ public class R820TTunerEditor extends TunerConfigurationEditor } ); mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); + add( new JLabel( "LNA:" ) ); add( mComboLNAGain ); /* VGA Gain Control */ mComboVGAGain = new JComboBox( R820TVGAGain.values() ); + mComboVGAGain.setEnabled( false ); mComboVGAGain.addActionListener( new ActionListener() { @Override @@ -441,7 +497,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor } ); mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " + "to MANUAL to enable adjustment" ); - mComboVGAGain.setEnabled( false ); add( new JLabel( "VGA:" ) ); add( mComboVGAGain ); } From 6bb417f69a868e926b28c6589591a230c68a9224 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 19 Mar 2016 14:49:55 -0400 Subject: [PATCH 62/74] wip: #34 - finishes the airspy tuner editor panel and adds tuner info button to the r820t tuner editor to view serial number and misc info. --- src/source/tuner/TunerEditor.java | 3 + .../tuner/airspy/AirspyInformationPanel.java | 77 -- .../airspy/AirspyTunerConfigurationPanel.java | 695 ----------------- .../tuner/airspy/AirspyTunerEditor.java | 720 ++++++++++++++++++ .../tuner/airspy/AirspyTunerEditorPanel.java | 112 --- .../TunerConfigurationEditor.java | 3 - .../TunerConfigurationFactory.java | 4 +- .../tuner/rtl/r820t/R820TTunerEditor.java | 85 ++- 8 files changed, 800 insertions(+), 899 deletions(-) delete mode 100644 src/source/tuner/airspy/AirspyInformationPanel.java delete mode 100644 src/source/tuner/airspy/AirspyTunerConfigurationPanel.java create mode 100644 src/source/tuner/airspy/AirspyTunerEditor.java delete mode 100644 src/source/tuner/airspy/AirspyTunerEditorPanel.java diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 6275174b..e1a2beb6 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -100,6 +100,7 @@ public class TunerEditor extends Editor listPanel.add( tableScroller, "span" ); mNewConfigurationButton.setEnabled( false ); + mNewConfigurationButton.setToolTipText( "Create a new configuration for the currently selected tuner" ); mNewConfigurationButton.addActionListener( new ActionListener() { @Override @@ -120,6 +121,7 @@ public class TunerEditor extends Editor listPanel.add( mNewConfigurationButton ); mDeleteConfigurationButton.setEnabled( false ); + mDeleteConfigurationButton.setToolTipText( "Deletes the currently selected tuner" ); mDeleteConfigurationButton.addActionListener( new ActionListener() { @Override @@ -140,6 +142,7 @@ public class TunerEditor extends Editor listPanel.add( mDeleteConfigurationButton ); mAssignConfigurationButton.setEnabled( false ); + mAssignConfigurationButton.setToolTipText( "Assigns to the currently selected tuner configuration to the selected tuner" ); mAssignConfigurationButton.addActionListener( new ActionListener() { @Override diff --git a/src/source/tuner/airspy/AirspyInformationPanel.java b/src/source/tuner/airspy/AirspyInformationPanel.java deleted file mode 100644 index 6b3ef2cb..00000000 --- a/src/source/tuner/airspy/AirspyInformationPanel.java +++ /dev/null @@ -1,77 +0,0 @@ -package source.tuner.airspy; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -public class AirspyInformationPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - private AirspyTunerController mController; - - /** - * Displays Airspy firmware, serial number and part identifier - */ - public AirspyInformationPanel( AirspyTunerController controller ) - { - mController = controller; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", - "[right,grow][grow]", - "[][][][][grow]" ) ); - - AirspyDeviceInformation info = mController.getDeviceInfo(); - - add( new JLabel( "Serial:" ) ); - add( new JLabel( info.getSerialNumber() ) ); - add( new JLabel( "Firmware:" ) ); - - //Split the firmware on spaces and use first value -- expose full - //string in a tool tip - String[] firmware = info.getVersion().split( " " ); - - JLabel firmwareLabel; - - if( firmware.length > 1 ) - { - firmwareLabel = new JLabel( firmware[ 0 ] ); - } - else - { - firmwareLabel = new JLabel( info.getVersion() ); - } - - firmwareLabel.setToolTipText( info.getVersion() ); - - add( firmwareLabel ); - add( new JLabel( "Part:" ) ); - add( new JLabel( info.getPartNumber() ) ); - add( new JLabel( "Board ID:" ) ); - add( new JLabel( info.getBoardID().getLabel() ) ); - } -} diff --git a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java b/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java deleted file mode 100644 index 60617e57..00000000 --- a/src/source/tuner/airspy/AirspyTunerConfigurationPanel.java +++ /dev/null @@ -1,695 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.airspy; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.util.List; - -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import javax.swing.JSlider; -import javax.swing.JTextField; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.airspy.AirspyTunerController.Gain; -import source.tuner.airspy.AirspyTunerController.GainMode; - -public class AirspyTunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( AirspyTunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - - private SettingsManager mSettingsManager; - private AirspyTunerController mController; - private AirspyTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JComboBox mSampleRateCombo; - - private JComboBox mGainModeCombo; - private JLabel mGainLabel; - private JLabel mGainFillerLabel; - private JLabel mGainValueLabel; - private JSlider mGain; - - private JLabel mIFGainLabel; - private JLabel mIFGainFillerLabel; - private JLabel mIFGainValueLabel; - private JSlider mIFGain; - - private JLabel mLNAGainLabel; - private JCheckBox mLNAAGC; - private JLabel mLNAGainValueLabel; - private JSlider mLNAGain; - - private JLabel mMixerGainValueLabel; - private JCheckBox mMixerAGC; - private JLabel mMixerGainLabel; - private JSlider mMixerGain; - - - public AirspyTunerConfigurationPanel( SettingsManager settingsManager, - AirspyTunerController controller ) - { - mSettingsManager = settingsManager; - mController = controller; - - init(); - } - - /** - * Initializes the gui components - */ - private void init() - { - setLayout( new MigLayout( "fill,wrap 4", "[left][grow][grow][right]", - "[][][][][][][grow]" ) ); - - /** - * Tuner configuration selector - */ - mComboConfigurations = new JComboBox(); - - mComboConfigurations.setModel( getModel() ); - - /* Determine which tuner configuration should be selected/displayed */ -// TunerConfigurationAssignment savedConfig = null; - String serial = null; - - serial = mController.getDeviceInfo().getSerialNumber(); - - if( serial != null ) - { -// savedConfig = mSettingsManager.getSelectedTunerConfiguration( -// TunerType.AIRSPY_R820T, serial ); - } - -// if( savedConfig != null ) -// { -// mSelectedConfig = getNamedConfiguration( -// savedConfig.getTunerConfigurationName() ); -// } - - /* If we couldn't determine the saved/selected config, use the first one */ - if( mSelectedConfig == null ) - { - mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - - //Store this config as the default for this tuner at this address - - if( serial != null ) - { -// mSettingsManager.setSelectedTunerConfiguration( TunerType.AIRSPY_R820T, -// serial, mSelectedConfig ); - } - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AirspyTunerConfiguration selected = - (AirspyTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "span,growx,push" ); - - add( new JSeparator(), "span,growx,push" ); - - - /** - * Tuner Configuration Name - */ - mName = new JTextField(); - mName.setText( mSelectedConfig.getName() ); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - mSelectedConfig.setName( mName.getText() ); - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mName, "span,growx,push" ); - - /** - * Sample Rate - */ - add( new JLabel( "Sample Rate:" ) ); - - List rates = mController.getSampleRates(); - - mSampleRateCombo = new JComboBox( - new DefaultComboBoxModel( rates.toArray( - new AirspySampleRate[ rates.size() ] ) ) ); - - mSampleRateCombo.setSelectedItem( mController.getSampleRate() ); - - mSampleRateCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem(); - - try - { - mController.setSampleRate( rate ); - mSelectedConfig.setSampleRate( rate.getRate() ); - save(); - } - catch ( LibUsbException | UsbException e1 ) - { - JOptionPane.showMessageDialog( AirspyTunerConfigurationPanel.this, - "Couldn't set sample rate to " + rate.getLabel() ); - - mLog.error( "Error setting airspy sample rate", e1 ); - } - } - } ); - - add( mSampleRateCombo, "span 3,growx" ); - - add( new JSeparator(), "span,growx,push" ); - - /** - * Gain Mode - */ - add( new JLabel( "Gain Mode:" ) ); - mGainModeCombo = new JComboBox( GainMode.values() ); - mGainModeCombo.setSelectedItem( Gain.getGainMode( mSelectedConfig.getGain() ) ); - mGainModeCombo.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); - updateGainComponents( mode ); - - switch( mode ) - { - case LINEARITY: - mGain.setValue( AirspyTunerController.LINEARITY_GAIN_DEFAULT.getValue() ); - mSelectedConfig.setGain( AirspyTunerController.LINEARITY_GAIN_DEFAULT ); - break; - case SENSITIVITY: - mGain.setValue( AirspyTunerController.SENSITIVITY_GAIN_DEFAULT.getValue() ); - mSelectedConfig.setGain( AirspyTunerController.SENSITIVITY_GAIN_DEFAULT ); - break; - case CUSTOM: - default: - mLNAGain.setValue( mSelectedConfig.getLNAGain() ); - mMixerGain.setValue( mSelectedConfig.getMixerGain() ); - mIFGain.setValue( mSelectedConfig.getIFGain() ); - mSelectedConfig.setGain( Gain.CUSTOM ); - break; - } - - save(); - } - } ); - - add( mGainModeCombo, "wrap" ); - - /** - * Gain - */ - mGainLabel = new JLabel( "Gain" ); - add( mGainLabel ); - mGainFillerLabel = new JLabel( "" ); - add( mGainFillerLabel, "span 2" ); - mGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getGain().getValue() ) ); - add( mGainValueLabel, "wrap" ); - mGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.GAIN_MIN, - AirspyTunerController.GAIN_MAX, - mSelectedConfig.getGain().getValue() ); - mGain.setMajorTickSpacing( 1 ); - mGain.setPaintTicks( true ); - - mGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); - int value = mGain.getValue(); - Gain gain = Gain.getGain( mode, value ); - - try - { - mController.setGain( gain ); - mSelectedConfig.setGain( gain ); - save(); - mGainValueLabel.setText( String.valueOf( gain.getValue() ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy gain to:" + gain.name(), e ); - - JOptionPane.showMessageDialog( mGain, - "Couldn't set gain value to " + gain.getValue() ); - - mGain.setValue( mSelectedConfig.getGain().getValue() ); - } - } - } ); - - add( mGain, "span,grow" ); - - /** - * IF/VGA Gain - */ - mIFGainLabel = new JLabel( "IF Gain" ); - add( mIFGainLabel ); - mIFGainFillerLabel = new JLabel( "" ); - add( mIFGainFillerLabel, "span 2" ); - mIFGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getIFGain() ) ); - add( mIFGainValueLabel, "wrap" ); - - mIFGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.IF_GAIN_MIN, - AirspyTunerController.IF_GAIN_MAX, - mSelectedConfig.getIFGain() ); - - mIFGain.setMajorTickSpacing( 1 ); - mIFGain.setPaintTicks( true ); - - mIFGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - int gain = mIFGain.getValue(); - - try - { - mController.setIFGain( gain ); - mSelectedConfig.setIFGain( gain ); - save(); - mIFGainValueLabel.setText( String.valueOf( gain ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy IF gain to:" + gain, e ); - - JOptionPane.showMessageDialog( mIFGain, - "Couldn't set IF gain value to " + gain ); - - mIFGain.setValue( mSelectedConfig.getIFGain() ); - } - } - } ); - - add( mIFGain, "span,grow" ); - - /** - * Mixer Gain - */ - mMixerGainLabel = new JLabel( "Mixer Gain" ); - add( mMixerGainLabel ); - - mMixerAGC = new JCheckBox( "AGC" ); - mMixerAGC.setSelected( mSelectedConfig.isMixerAGC() ); - mMixerAGC.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - try - { - mController.setMixerAGC( !mSelectedConfig.isMixerAGC() ); - mSelectedConfig.setMixerAGC( !mSelectedConfig.isMixerAGC() ); - save(); - } - catch ( Exception e1 ) - { - mLog.error( "Error setting Mixer AGC Enabled:" + !mSelectedConfig.isMixerAGC() ); - mMixerAGC.setSelected( mSelectedConfig.isMixerAGC() ); - } - } - } ); - - - add( mMixerAGC, "span 2,center" ); - - mMixerGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getMixerGain() ) ); - add( mMixerGainValueLabel, "wrap" ); - - mMixerGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.MIXER_GAIN_MIN, - AirspyTunerController.MIXER_GAIN_MAX, - mSelectedConfig.getMixerGain() ); - - mMixerGain.setMajorTickSpacing( 1 ); - mMixerGain.setPaintTicks( true ); - - mMixerGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - int gain = mMixerGain.getValue(); - - try - { - mController.setMixerGain( gain ); - mSelectedConfig.setMixerGain( gain ); - save(); - mMixerGainValueLabel.setText( String.valueOf( gain ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy Mixer gain to:" + gain, e ); - - JOptionPane.showMessageDialog( mIFGain, "Couldn't set Mixer gain value to " + gain ); - - mMixerGain.setValue( mSelectedConfig.getMixerGain() ); - } - } - } ); - - add( mMixerGain, "span,grow" ); - - /** - * LNA Gain - */ - mLNAGainLabel = new JLabel( "LNA Gain" ); - add( mLNAGainLabel ); - - mLNAAGC = new JCheckBox( "AGC" ); - mLNAAGC.setSelected( mSelectedConfig.isLNAAGC() ); - mLNAAGC.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - try - { - mController.setLNAAGC( !mSelectedConfig.isLNAAGC() ); - mSelectedConfig.setLNAAGC( !mSelectedConfig.isLNAAGC() ); - save(); - } - catch ( Exception e1 ) - { - mLog.error( "Error setting LNA AGC Enabled:" + !mSelectedConfig.isLNAAGC() ); - mLNAAGC.setSelected( mSelectedConfig.isLNAAGC() ); - } - - } - } ); - - add( mLNAAGC, "span 2,center" ); - - mLNAGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getLNAGain() ) ); - add( mLNAGainValueLabel, "wrap" ); - - mLNAGain = new JSlider( JSlider.HORIZONTAL, - AirspyTunerController.LNA_GAIN_MIN, - AirspyTunerController.LNA_GAIN_MAX, - mSelectedConfig.getLNAGain() ); - - mLNAGain.setMajorTickSpacing( 1 ); - mLNAGain.setPaintTicks( true ); - - mLNAGain.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent event ) - { - int gain = mLNAGain.getValue(); - - try - { - mController.setLNAGain( gain ); - mSelectedConfig.setLNAGain( gain ); - save(); - mLNAGainValueLabel.setText( String.valueOf( gain ) ); - } - catch ( Exception e ) - { - mLog.error( "Couldn't set airspy LNA gain to:" + gain, e ); - - JOptionPane.showMessageDialog( mIFGain, "Couldn't set LNA gain value to " + gain ); - - mLNAGain.setValue( mSelectedConfig.getLNAGain() ); - } - } - } ); - - add( mLNAGain, "span,grow" ); - - //Set the enabled state of each of the gain controls - updateGainComponents( (GainMode)mGainModeCombo.getSelectedItem() ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { -// TunerConfiguration config = -// mSettingsManager.addNewTunerConfiguration( -// TunerType.AIRSPY_R820T, -// "New Configuration" ); -// -// mComboConfigurations.setModel( getModel() ); -// -// mComboConfigurations.setSelectedItem( config ); -// -// repaint(); - } - } ); - - add( mNewConfiguration, "span 2,growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - AirspyTunerConfiguration selected = - (AirspyTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - AirspyTunerConfigurationPanel.this, - "Are you sure you want to delete '" - + selected.getName() + "'?", - "Are you sure?", - JOptionPane.YES_NO_OPTION ); - - if( n == JOptionPane.YES_OPTION ) - { -// mSettingsManager.deleteTunerConfiguration( selected ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedIndex( 0 ); - - repaint(); - } - } - } - } ); - - add( mDeleteConfiguration, "span 2,growx,push" ); - } - - /** - * Updates the enabled state of each of the gain controls according to the - * specified gain mode. The overall gain controls are enabled for linearity - * and sensitivity and the individual gain controls are disabled, and - * vice-versa for custom mode. - */ - private void updateGainComponents( GainMode mode ) - { - boolean isCustom = ( mode == GainMode.CUSTOM ); - - mGainLabel.setEnabled( !isCustom ); - mGainFillerLabel.setEnabled( !isCustom ); - mGainValueLabel.setEnabled( !isCustom ); - mGain.setEnabled( !isCustom ); - mIFGainLabel.setEnabled( isCustom ); - mIFGainFillerLabel.setEnabled( isCustom ); - mIFGainValueLabel.setEnabled( isCustom ); - mIFGain.setEnabled( isCustom ); - mLNAGainLabel.setEnabled( isCustom ); - mLNAAGC.setEnabled( isCustom ); - mLNAGainValueLabel.setEnabled( isCustom ); - mLNAGain.setEnabled( isCustom ); - mMixerGainValueLabel.setEnabled( isCustom ); - mMixerAGC.setEnabled( isCustom ); - mMixerGainLabel.setEnabled( isCustom ); - mMixerGain.setEnabled( isCustom ); - } - - /** - * Updates gui controls with the values from the tuner configuration - * @param config - tuner configuration - */ - private void update( AirspyTunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - - AirspySampleRate rate = mController.getSampleRate( - mSelectedConfig.getSampleRate() ); - - if( rate != null ) - { - mSampleRateCombo.setSelectedItem( rate ); - } - - mIFGain.setValue( mSelectedConfig.getIFGain() ); - mMixerGain.setValue( mSelectedConfig.getMixerGain() ); - mLNAGain.setValue( mSelectedConfig.getLNAGain() ); - - mMixerAGC.setSelected( mSelectedConfig.isMixerAGC() ); - mLNAAGC.setSelected( mSelectedConfig.isLNAAGC() ); - - Gain gain = mSelectedConfig.getGain(); - mGainModeCombo.setSelectedItem( Gain.getGainMode( gain ) ); - mGain.setValue( gain.getValue() ); - -// mSettingsManager.setSelectedTunerConfiguration( -// TunerType.AIRSPY_R820T, mController.getDeviceInfo().getSerialNumber(), config ); - - save(); - } - catch ( /*UsbException |*/ SourceException e1 ) - { - JOptionPane.showMessageDialog( - AirspyTunerConfigurationPanel.this, - "Airspy Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "Airspy Tuner Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - /** - * Constructs a combo box model for the tuner configuration combo component - * by assembling a list of the tuner configurations appropriate for this - * specific tuner - */ - private ComboBoxModel getModel() - { -// ArrayList configs = -// mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - -// for( TunerConfiguration config: configs ) -// { -// model.addElement( (AirspyTunerConfiguration)config ); -// } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private AirspyTunerConfiguration getNamedConfiguration( String name ) - { -// ArrayList configs = -// mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T ); - -// for( TunerConfiguration config: configs ) -// { -// if( config.getName().contentEquals( name ) ) -// { -// return (AirspyTunerConfiguration)config; -// } -// } - - return null; - } - - /** - * Saves current tuner configuration settings - */ - private void save() - { -// mSettingsManager.save(); - } -} diff --git a/src/source/tuner/airspy/AirspyTunerEditor.java b/src/source/tuner/airspy/AirspyTunerEditor.java new file mode 100644 index 00000000..36066eaa --- /dev/null +++ b/src/source/tuner/airspy/AirspyTunerEditor.java @@ -0,0 +1,720 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2015-2016 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 + ******************************************************************************/ +package source.tuner.airspy; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; +import java.util.List; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.usb.UsbException; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.LibUsbException; + +import source.SourceException; +import source.tuner.airspy.AirspyTunerController.Gain; +import source.tuner.airspy.AirspyTunerController.GainMode; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationModel; + +public class AirspyTunerEditor extends TunerConfigurationEditor +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = + LoggerFactory.getLogger( AirspyTunerEditor.class ); + + private JTextField mConfigurationName; + private JButton mTunerInfo; + + private JComboBox mSampleRateCombo; + private JSpinner mFrequencyCorrection; + + private JComboBox mGainModeCombo; + + private JSlider mMasterGain; + private JLabel mMasterGainValueLabel; + + private JSlider mIFGain; + private JLabel mIFGainValueLabel; + + private JSlider mLNAGain; + private JLabel mLNAGainValueLabel; + + private JSlider mMixerGain; + private JLabel mMixerGainValueLabel; + + private JCheckBox mLNAAGC; + private JCheckBox mMixerAGC; + + private AirspyTunerController mController; + private boolean mLoading; + + public AirspyTunerEditor( TunerConfigurationModel tunerConfigurationModel, + AirspyTuner tuner ) + { + super( tunerConfigurationModel ); + mController = tuner.getController(); + + init(); + } + + private AirspyTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (AirspyTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][grow]" ) ); + + add( new JLabel( "Airspy Tuner Configuration" ), "span,align center" ); + +// tabs.add( "Info", new AirspyInformationPanel( mController ) ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( AirspyTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + /** + * Sample Rate + */ + add( new JLabel( "Sample Rate:" ) ); + + List rates = mController.getSampleRates(); + + mSampleRateCombo = new JComboBox( + new DefaultComboBoxModel( rates.toArray( + new AirspySampleRate[ rates.size() ] ) ) ); + mSampleRateCombo.setEnabled( false ); + mSampleRateCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem(); + + try + { + mController.setSampleRate( rate ); + save(); + } + catch ( LibUsbException | UsbException e1 ) + { + JOptionPane.showMessageDialog( AirspyTunerEditor.this, + "Couldn't set sample rate to " + rate.getLabel() ); + + mLog.error( "Error setting airspy sample rate", e1 ); + } + } + } ); + + add( mSampleRateCombo ); + + /** + * Frequency Correction + */ + SpinnerModel model = + new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + } + catch ( SourceException e1 ) + { + mLog.error( "Error setting frequency correction value", e1 ); + } + + save(); + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + + add( new JSeparator(), "span,growx,push" ); + + /** + * Gain Mode + */ + add( new JLabel( "Gain Mode:" ) ); + mGainModeCombo = new JComboBox( GainMode.values() ); + mGainModeCombo.setEnabled( false ); + mGainModeCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); + + updateGainComponents( mode ); + + if( hasItem() ) + { + AirspyTunerConfiguration airspy = getConfiguration(); + + Gain gain = airspy.getGain(); + + if( mode == GainMode.CUSTOM ) + { + mIFGain.setValue( airspy.getIFGain() ); + mMixerGain.setValue( airspy.getMixerGain() ); + mMixerAGC.setSelected( airspy.isMixerAGC() ); + mLNAGain.setValue( airspy.getLNAGain() ); + mLNAAGC.setSelected( airspy.isLNAAGC() ); + } + else + { + mMixerAGC.setSelected( false ); + mLNAAGC.setSelected( false ); + mMasterGain.setValue( gain.getValue() ); + mIFGain.setValue( gain.getIF() ); + mMixerGain.setValue( gain.getMixer() ); + mLNAGain.setValue( gain.getLNA() ); + } + } + + save(); + } + } ); + + add( mGainModeCombo, "wrap" ); + + /** + * Gain + */ + add( new JLabel( "Master:" ) ); + + mMasterGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.GAIN_MIN, + AirspyTunerController.GAIN_MAX, + AirspyTunerController.GAIN_MIN ); + mMasterGain.setMajorTickSpacing( 1 ); + mMasterGain.setPaintTicks( true ); + + mMasterGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); + int value = mMasterGain.getValue(); + Gain gain = Gain.getGain( mode, value ); + + try + { + mController.setGain( gain ); + save(); + mMasterGainValueLabel.setText( String.valueOf( gain.getValue() ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy gain to:" + gain.name(), e ); + JOptionPane.showMessageDialog( mMasterGain, "Couldn't set gain value to " + + gain.getValue() ); + } + } + } ); + + add( mMasterGain, "span 2" ); + + mMasterGainValueLabel = new JLabel( "0" ); + add( mMasterGainValueLabel ); + + add( new JLabel( "IF:" ) ); + + mIFGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.IF_GAIN_MIN, + AirspyTunerController.IF_GAIN_MAX, + AirspyTunerController.IF_GAIN_MIN ); + + mIFGain.setMajorTickSpacing( 1 ); + mIFGain.setPaintTicks( true ); + + mIFGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + int gain = mIFGain.getValue(); + + try + { + mController.setIFGain( gain ); + save(); + mIFGainValueLabel.setText( String.valueOf( gain ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy IF gain to:" + gain, e ); + + JOptionPane.showMessageDialog( mIFGain, "Couldn't set IF gain value to " + gain ); + } + } + } ); + + add( mIFGain, "span 2" ); + + mIFGainValueLabel = new JLabel( "0" ); + add( mIFGainValueLabel ); + + /** + * Mixer Gain + */ + add( new JLabel( "Mixer:" ) ); + + mMixerGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.MIXER_GAIN_MIN, + AirspyTunerController.MIXER_GAIN_MAX, + AirspyTunerController.MIXER_GAIN_MIN ); + + mMixerGain.setMajorTickSpacing( 1 ); + mMixerGain.setPaintTicks( true ); + + mMixerGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + int gain = mMixerGain.getValue(); + + try + { + mController.setMixerGain( gain ); + save(); + mMixerGainValueLabel.setText( String.valueOf( gain ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy Mixer gain to:" + gain, e ); + JOptionPane.showMessageDialog( mIFGain, "Couldn't set Mixer gain value to " + gain ); + } + } + } ); + + add( mMixerGain, "span 2" ); + + mMixerGainValueLabel = new JLabel( "0" ); + add( mMixerGainValueLabel ); + + add( new JLabel( "LNA:" ) ); + + mLNAGain = new JSlider( JSlider.HORIZONTAL, + AirspyTunerController.LNA_GAIN_MIN, + AirspyTunerController.LNA_GAIN_MAX, + AirspyTunerController.LNA_GAIN_MIN ); + + mLNAGain.setMajorTickSpacing( 1 ); + mLNAGain.setPaintTicks( true ); + + mLNAGain.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent event ) + { + int gain = mLNAGain.getValue(); + + try + { + mController.setLNAGain( gain ); + save(); + mLNAGainValueLabel.setText( String.valueOf( gain ) ); + } + catch ( Exception e ) + { + mLog.error( "Couldn't set airspy LNA gain to:" + gain, e ); + JOptionPane.showMessageDialog( mIFGain, "Couldn't set LNA gain value to " + gain ); + } + } + } ); + + add( mLNAGain, "span 2" ); + + mLNAGainValueLabel = new JLabel( "0" ); + add( mLNAGainValueLabel ); + + mMixerAGC = new JCheckBox( "Mixer AGC" ); + mMixerAGC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + try + { + mController.setMixerAGC( mMixerAGC.isSelected() ); + save(); + } + catch ( Exception e1 ) + { + mLog.error( "Error setting Mixer AGC Enabled" ); + } + } + } ); + + add( mMixerAGC, "span 2,center" ); + + mLNAAGC = new JCheckBox( "LNA AGC" ); + mLNAAGC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + try + { + mController.setLNAAGC( mLNAAGC.isSelected() ); + save(); + } + catch ( Exception e1 ) + { + mLog.error( "Error setting LNA AGC Enabled" ); + } + + } + } ); + + add( mLNAAGC, "span 2,center" ); + } + + /** + * Updates the enabled state of each of the gain controls according to the + * specified gain mode. The master gain control is enabled for linearity + * and sensitivity and the individual gain controls are disabled, and + * vice-versa for custom mode. + */ + private void updateGainComponents( GainMode mode ) + { + switch( mode ) + { + case LINEARITY: + if( !mMasterGain.isEnabled() ) + { + mMasterGain.setEnabled( true ); + mMasterGainValueLabel.setEnabled( true ); + } + if( mIFGain.isEnabled() ) + { + mIFGain.setEnabled( false ); + mIFGainValueLabel.setEnabled( false ); + } + if( mLNAGain.isEnabled() ) + { + mLNAAGC.setEnabled( false ); + mLNAGainValueLabel.setEnabled( false ); + mLNAGain.setEnabled( false ); + } + if( mMixerGain.isEnabled() ) + { + mMixerGainValueLabel.setEnabled( false ); + mMixerAGC.setEnabled( false ); + mMixerGain.setEnabled( false ); + } + break; + case SENSITIVITY: + if( !mMasterGain.isEnabled() ) + { + mMasterGain.setEnabled( true ); + mMasterGainValueLabel.setEnabled( true ); + } + if( mIFGain.isEnabled() ) + { + mIFGain.setEnabled( false ); + mIFGainValueLabel.setEnabled( false ); + } + if( mLNAGain.isEnabled() ) + { + mLNAAGC.setEnabled( false ); + mLNAGainValueLabel.setEnabled( false ); + mLNAGain.setEnabled( false ); + } + if( mMixerGain.isEnabled() ) + { + mMixerGainValueLabel.setEnabled( false ); + mMixerAGC.setEnabled( false ); + mMixerGain.setEnabled( false ); + } + break; + case CUSTOM: + if( mMasterGain.isEnabled() ) + { + mMasterGain.setEnabled( false ); + mMasterGainValueLabel.setEnabled( false ); + } + if( !mIFGain.isEnabled() ) + { + mIFGain.setEnabled( true ); + mIFGainValueLabel.setEnabled( true ); + } + if( !mLNAGain.isEnabled() ) + { + mLNAAGC.setEnabled( true ); + mLNAGainValueLabel.setEnabled( true ); + mLNAGain.setEnabled( true ); + } + if( !mMixerGain.isEnabled() ) + { + mMixerGainValueLabel.setEnabled( true ); + mMixerAGC.setEnabled( true ); + mMixerGain.setEnabled( true ); + } + default: + break; + } + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + AirspyTunerConfiguration config = getConfiguration(); + config.setName( mConfigurationName.getText() ); + config.setSampleRate( ((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setGain( Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ) ); + config.setIFGain( mIFGain.getValue() ); + config.setMixerGain( mMixerGain.getValue() ); + config.setLNAGain( mLNAGain.getValue() ); + config.setMixerAGC( mMixerAGC.isSelected() ); + config.setLNAAGC( mLNAAGC.isSelected() ); + } + } + + private AirspySampleRate fromValue( int value ) + { + List rates = mController.getSampleRates(); + + for( AirspySampleRate rate: rates ) + { + if( rate.getRate() == value ) + { + return rate; + } + } + + if( rates.size() > 0 ) + { + return rates.get( 0 ); + } + + return null; + } + + /** + * Sets all controls to the argument enabled state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mSampleRateCombo.isEnabled() != enabled ) + { + mSampleRateCombo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mGainModeCombo.isEnabled() != enabled ) + { + mGainModeCombo.setEnabled( enabled ); + } + + if( mMasterGain.isEnabled() != enabled ) + { + mMasterGain.setEnabled( enabled ); + } + + if( mIFGain.isEnabled() != enabled ) + { + mIFGain.setEnabled( enabled ); + } + + if( mMixerGain.isEnabled() != enabled ) + { + mMixerGain.setEnabled( enabled ); + } + + if( mMixerAGC.isEnabled() != enabled ) + { + mMixerAGC.setEnabled( enabled ); + } + + if( mLNAGain.isEnabled() != enabled ) + { + mLNAGain.setEnabled( enabled ); + } + + if( mLNAAGC.isEnabled() != enabled ) + { + mLNAAGC.setEnabled( enabled ); + } + } + + /** + * Applies the tuner configuration to the editor. If the configuration is assigned to the + * currently selected tuner, then the settings are applied. + */ + @Override + public void setItem( TunerConfiguration config ) + { + super.setItem( config ); + + if( hasItem() && getItem().isAssigned() ) + { + AirspyTunerConfiguration airspy = getConfiguration(); + + setControlsEnabled( true ); + + mConfigurationName.setText( airspy.getName() ); + + AirspySampleRate rate = fromValue( airspy.getSampleRate() ); + + if( rate != null ) + { + mSampleRateCombo.setSelectedItem( rate ); + } + + mFrequencyCorrection.setValue( airspy.getFrequencyCorrection() ); + + GainMode mode = Gain.getGainMode( airspy.getGain() ); + + //This will set the master, IF, Mixer, LNA and AGC controls + mGainModeCombo.setSelectedItem( mode ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( hasItem() ? getItem().getName() : "" ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + AirspyDeviceInformation info = mController.getDeviceInfo(); + + sb.append( "

    Airspy Tuner

    " ); + sb.append( "Serial: " ); + sb.append( info.getSerialNumber() ); + sb.append( "
    " ); + + sb.append( "Firmware: " ); + String[] firmware = info.getVersion().split( " " ); + sb.append( firmware.length > 1 ? firmware[ 0 ] : info.getVersion() ); + sb.append( "
    " ); + + sb.append( "Part: " ); + sb.append( info.getPartNumber() ); + sb.append( "
    " ); + + sb.append( "Board ID: " ); + sb.append( info.getBoardID().getLabel() ); + sb.append( "
    " ); + + return sb.toString(); + } +} diff --git a/src/source/tuner/airspy/AirspyTunerEditorPanel.java b/src/source/tuner/airspy/AirspyTunerEditorPanel.java deleted file mode 100644 index 3cc93d89..00000000 --- a/src/source/tuner/airspy/AirspyTunerEditorPanel.java +++ /dev/null @@ -1,112 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2015 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 - ******************************************************************************/ -package source.tuner.airspy; - -import gui.control.JFrequencyControl; - -import java.awt.Color; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.IFrequencyChangeProcessor; - -import com.jidesoft.swing.JideTabbedPane; - -public class AirspyTunerEditorPanel extends JPanel - implements IFrequencyChangeProcessor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = - LoggerFactory.getLogger( AirspyTunerEditorPanel.class ); - - private JFrequencyControl mFrequencyControl; - - private AirspyTuner mTuner; - private AirspyTunerController mController; - private SettingsManager mSettingsManager; - - public AirspyTunerEditorPanel( AirspyTuner tuner, - SettingsManager settingsManager ) - { - mTuner = tuner; - mController = mTuner.getController(); - mSettingsManager = settingsManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", - "[right,grow][grow]", - "[][][][][][grow]" ) ); - - add( new JLabel( "Airspy Tuner" ), "span,align center" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as frequency change listener. This creates a - * feedback loop, so the control does not rebroadcast the event */ - mTuner.getTunerController().addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span,align center" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - add( tabs, "span,grow,push" ); - - tabs.add( "Config", new AirspyTunerConfigurationPanel( - mSettingsManager, mTuner.getController() ) ); - - tabs.add( "Info", new AirspyInformationPanel( mTuner.getController() ) ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "error setting frequency [" + - event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/configuration/TunerConfigurationEditor.java b/src/source/tuner/configuration/TunerConfigurationEditor.java index 2c0908c5..1c02f4b0 100644 --- a/src/source/tuner/configuration/TunerConfigurationEditor.java +++ b/src/source/tuner/configuration/TunerConfigurationEditor.java @@ -1,7 +1,6 @@ package source.tuner.configuration; import gui.editor.Editor; -import source.tuner.TunerType; public abstract class TunerConfigurationEditor extends Editor { @@ -14,8 +13,6 @@ public abstract class TunerConfigurationEditor extends Editor mComboMasterGain; private JComboBox mComboMixerGain; @@ -84,12 +88,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor init(); } - @Override - public boolean canEdit( TunerType tunerType ) - { - return tunerType == TunerType.RAFAELMICRO_R820T; - } - @Override public void save() { @@ -169,6 +167,11 @@ public class R820TTunerEditor extends TunerConfigurationEditor mConfigurationName.setEnabled( enabled ); } + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); @@ -213,9 +216,9 @@ public class R820TTunerEditor extends TunerConfigurationEditor private void init() { setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", - "[][][][][][][grow]" ) ); + "[][][][][][][][grow]" ) ); - add( new JLabel( "R820T Tuner Configuration" ), "span, align center" ); + add( new JLabel( "R820T Tuner Configuration" ), "span,align center" ); /** * Tuner Configuration Name @@ -234,7 +237,21 @@ public class R820TTunerEditor extends TunerConfigurationEditor } ); add( new JLabel( "Name:" ) ); - add( mConfigurationName, "span,grow" ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( R820TTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + mComboSampleRate = new JComboBox<>( SampleRate.values() ); mComboSampleRate.setEnabled( false ); @@ -312,10 +329,12 @@ public class R820TTunerEditor extends TunerConfigurationEditor add( mFrequencyCorrection ); add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); - + /** * Gain Controls */ + add( new JLabel( "Gain" ), "span" ); + /* Master Gain Control */ mComboMasterGain = new JComboBox( R820TGain.values() ); mComboMasterGain.setEnabled( false ); @@ -500,4 +519,48 @@ public class R820TTunerEditor extends TunerConfigurationEditor add( new JLabel( "VGA:" ) ); add( mComboVGAGain ); } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + Descriptor descriptor = mController.getDescriptor(); + + sb.append( "

    RTL-2832 with R820T Tuner

    " ); + + if( descriptor == null ) + { + sb.append( "No EEPROM Descriptor Available" ); + } + else + { + sb.append( "USB ID: " ); + sb.append( descriptor.getVendorID() ); + sb.append( ":" ); + sb.append( descriptor.getProductID() ); + sb.append( "
    " ); + + sb.append( "Vendor: " ); + sb.append( descriptor.getVendorLabel() ); + sb.append( "
    " ); + + sb.append( "Product: " ); + sb.append( descriptor.getProductLabel() ); + sb.append( "
    " ); + + sb.append( "Serial: " ); + sb.append( descriptor.getSerial() ); + sb.append( "
    " ); + + sb.append( "IR Enabled: " ); + sb.append( descriptor.irEnabled() ); + sb.append( "
    " ); + + sb.append( "Remote Wake: " ); + sb.append( descriptor.remoteWakeupEnabled() ); + sb.append( "
    " ); + } + + return sb.toString(); + } } From d3e8816bb4bfbdc526a774257dbfa3189ba312c0 Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Mar 2016 05:30:16 -0400 Subject: [PATCH 63/74] wip: #34 Adds the E4000 tuner editor and updates a few other tuner editor pieces. --- .../tuner/airspy/AirspyTunerEditor.java | 7 +- .../TunerConfigurationFactory.java | 19 +- .../TunerConfigurationModel.java | 2 - .../tuner/rtl/e4k/E4KTunerConfiguration.java | 12 +- .../tuner/rtl/e4k/E4KTunerController.java | 16 +- src/source/tuner/rtl/e4k/E4KTunerEditor.java | 494 ++++++++++++++++++ .../tuner/rtl/e4k/E4KTunerEditorPanel.java | 122 ----- .../tuner/rtl/r820t/R820TTunerEditor.java | 236 +++++---- 8 files changed, 635 insertions(+), 273 deletions(-) create mode 100644 src/source/tuner/rtl/e4k/E4KTunerEditor.java delete mode 100644 src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java diff --git a/src/source/tuner/airspy/AirspyTunerEditor.java b/src/source/tuner/airspy/AirspyTunerEditor.java index 36066eaa..ee034ca3 100644 --- a/src/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/source/tuner/airspy/AirspyTunerEditor.java @@ -52,7 +52,9 @@ import source.tuner.airspy.AirspyTunerController.Gain; import source.tuner.airspy.AirspyTunerController.GainMode; import source.tuner.configuration.TunerConfiguration; import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.configuration.TunerConfigurationEvent.Event; public class AirspyTunerEditor extends TunerConfigurationEditor { @@ -113,8 +115,6 @@ public class AirspyTunerEditor extends TunerConfigurationEditor add( new JLabel( "Airspy Tuner Configuration" ), "span,align center" ); -// tabs.add( "Info", new AirspyInformationPanel( mController ) ); - mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); mConfigurationName.addFocusListener( new FocusListener() @@ -569,6 +569,9 @@ public class AirspyTunerEditor extends TunerConfigurationEditor config.setLNAGain( mLNAGain.getValue() ); config.setMixerAGC( mMixerAGC.isSelected() ); config.setLNAAGC( mLNAAGC.isSelected() ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); } } diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java index a8db8333..7684f820 100644 --- a/src/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -12,11 +12,15 @@ import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; import source.tuner.hackrf.HackRFTunerConfiguration; import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.e4k.E4KTunerConfiguration; +import source.tuner.rtl.e4k.E4KTunerEditor; import source.tuner.rtl.r820t.R820TTunerConfiguration; import source.tuner.rtl.r820t.R820TTunerEditor; public class TunerConfigurationFactory { + /** + * Creates a tuner configuration for the specified tuner type, unique ID and name + */ public static TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID, String name ) { @@ -41,6 +45,9 @@ public class TunerConfigurationFactory } } + /** + * Creates a tuner editor gui for the specified tuner + */ public static Editor getEditor( Tuner tuner, TunerConfigurationModel model ) { @@ -49,20 +56,18 @@ public class TunerConfigurationFactory case AIRSPY_R820T: return new AirspyTunerEditor( model, (AirspyTuner)tuner ); case ELONICS_E4000: - break; + return new E4KTunerEditor( model, (RTL2832Tuner)tuner ); case FUNCUBE_DONGLE_PRO: - break; + return new EmptyEditor( "a tuner" ); case FUNCUBE_DONGLE_PRO_PLUS: - break; + return new EmptyEditor( "a tuner" ); case HACKRF: - break; + return new EmptyEditor( "a tuner" ); case RAFAELMICRO_R820T: return new R820TTunerEditor( model, (RTL2832Tuner)tuner ); case UNKNOWN: default: - break; + throw new IllegalArgumentException( "Unrecognized Tuner: " + tuner.getName() ); } - - return new EmptyEditor( "a tuner" ); } } diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index 44f4008f..daa37793 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -153,7 +153,6 @@ public class TunerConfigurationModel extends AbstractTableModel { if( config.isAssigned() ) { - mLog.debug( "Unassigning: " + config.getName() ); config.setAssigned( false ); int index = mTunerConfigurations.indexOf( config ); @@ -162,7 +161,6 @@ public class TunerConfigurationModel extends AbstractTableModel } } - mLog.debug( "Assigning: " + configToAssign.getName() ); configToAssign.setAssigned( true ); int index = mTunerConfigurations.indexOf( configToAssign ); fireTableCellUpdated( index, ASSIGNED ); diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java index 072d2fa6..3d4c9eff 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java +++ b/src/source/tuner/rtl/e4k/E4KTunerConfiguration.java @@ -29,12 +29,12 @@ import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; public class E4KTunerConfiguration extends TunerConfiguration { - private E4KGain mMasterGain = E4KGain.AUTOMATIC; - private E4KMixerGain mMixerGain = E4KMixerGain.AUTOMATIC; - private E4KLNAGain mLNAGain = E4KLNAGain.AUTOMATIC; - private E4KEnhanceGain mEnhanceGain = E4KEnhanceGain.AUTOMATIC; - private double mFrequencyCorrection = 8.9d; - private SampleRate mSampleRate = SampleRate.RATE_1_920MHZ; + private E4KGain mMasterGain = E4KGain.MANUAL; + private E4KMixerGain mMixerGain = E4KMixerGain.GAIN_4; + private E4KLNAGain mLNAGain = E4KLNAGain.GAIN_PLUS_200; + private E4KEnhanceGain mEnhanceGain = E4KEnhanceGain.GAIN_3; + private double mFrequencyCorrection = 0.0d; + private SampleRate mSampleRate = SampleRate.RATE_2_400MHZ; /** * Default constructor for JAXB diff --git a/src/source/tuner/rtl/e4k/E4KTunerController.java b/src/source/tuner/rtl/e4k/E4KTunerController.java index 6f4df48c..a5f92bda 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerController.java +++ b/src/source/tuner/rtl/e4k/E4KTunerController.java @@ -19,7 +19,6 @@ package source.tuner.rtl.e4k; import java.util.Arrays; -import javax.swing.JPanel; import javax.usb.UsbException; import org.slf4j.Logger; @@ -30,7 +29,6 @@ import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; import org.usb4java.LibUsbException; -import settings.SettingsManager; import source.SourceException; import source.tuner.TunerType; import source.tuner.configuration.TunerConfiguration; @@ -110,8 +108,6 @@ public class E4KTunerController extends RTL2832TunerController public static final byte CHFCALIB_CMD = (byte)0x01; - private E4KTunerEditorPanel mEditor; - public E4KTunerController( Device device, DeviceDescriptor deviceDescriptor, ThreadPoolManager threadPoolManager ) @@ -252,16 +248,6 @@ public class E4KTunerController extends RTL2832TunerController } } - public JPanel getEditor( SettingsManager settingsManager ) - { - if( mEditor == null ) - { - mEditor = new E4KTunerEditorPanel( this, settingsManager ); - } - - return mEditor; - } - /** * Sets the IF filters (mixer, channel and RC) to the correct filter setting * for the selected bandwidth/sample rate @@ -1070,7 +1056,7 @@ public class E4KTunerController extends RTL2832TunerController (byte)( ( temp & ~mask ) | ( value & mask ) ), controlI2CRepeater ); - int temp2 = readE4KRegister( register, controlI2CRepeater ); + readE4KRegister( register, controlI2CRepeater ); } } diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/source/tuner/rtl/e4k/E4KTunerEditor.java new file mode 100644 index 00000000..67cfb310 --- /dev/null +++ b/src/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -0,0 +1,494 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source.tuner.rtl.e4k; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.usb.UsbException; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.LibUsbException; + +import source.SourceException; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.rtl.RTL2832Tuner; +import source.tuner.rtl.RTL2832TunerController.Descriptor; +import source.tuner.rtl.RTL2832TunerController.SampleRate; +import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; +import source.tuner.rtl.e4k.E4KTunerController.E4KGain; +import source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; +import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; + +public class E4KTunerEditor extends TunerConfigurationEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( E4KTunerEditor.class ); + + private static final long serialVersionUID = 1L; + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JComboBox mComboMasterGain; + private JComboBox mComboMixerGain; + private JComboBox mComboLNAGain; + private JComboBox mComboEnhanceGain; + + private E4KTunerController mController; + private boolean mLoading; + + public E4KTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) + { + super( tunerConfigurationModel ); + mController = (E4KTunerController)tuner.getController(); + + init(); + } + + private E4KTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (E4KTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][grow]" ) ); + + add( new JLabel( "E4000 Tuner Configuration" ), "span,align center" ); + + /** + * Tuner Configuration Name + */ + add( new JLabel( "Name:" ) ); + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SampleRate sampleRate = (SampleRate)mComboSampleRate.getSelectedItem(); + + try + { + mController.setSampleRate( sampleRate ); + save(); + } + catch ( SourceException | LibUsbException eSampleRate ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, + "E4K Tuner Controller - couldn't apply the sample rate setting [" + + sampleRate.getLabel() + "] " + eSampleRate.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", eSampleRate ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); + + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply frequency correction value: " + value + e1.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply frequency correction value:" + + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + + mComboMasterGain = new JComboBox( E4KGain.values() ); + mComboMasterGain.setEnabled( false ); + mComboMasterGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); + + mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); + + if( gain == E4KGain.MANUAL ) + { + mComboMixerGain.setSelectedItem( mController.getMixerGain( true ) ); + mComboMixerGain.setEnabled( true ); + + mComboLNAGain.setSelectedItem( mController.getLNAGain( true ) ); + mComboLNAGain.setEnabled( true ); + + mComboEnhanceGain.setSelectedItem( mController.getEnhanceGain( true ) ); + mComboEnhanceGain.setEnabled( true ); + } + else + { + mComboMixerGain.setEnabled( false ); + mComboMixerGain.setSelectedItem( gain.getMixerGain() ); + + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setSelectedItem( gain.getLNAGain() ); + + mComboEnhanceGain.setEnabled( false ); + mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); + } + + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply gain setting", e ); + } + } + } ); + + mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " + + "gain, MANUAL to enable
    independent control of " + + "Mixer, LNA and Enhance gain
    " + + "settings, or one of the individual gain settings for
    " + + "semi-manual gain control" ); + add( new JLabel( "Master" ) ); + add( mComboMasterGain ); + + mComboMixerGain = new JComboBox( E4KMixerGain.values() ); + mComboMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KMixerGain mixerGain = (E4KMixerGain)mComboMixerGain.getSelectedItem(); + mController.setMixerGain( mixerGain, true ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the mixer gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply mixer gain setting", e ); + } + } + } ); + mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + + "to MASTER to enable adjustment" ); + mComboMixerGain.setEnabled( false ); + add( new JLabel( "Mixer" ) ); + add( mComboMixerGain ); + + mComboLNAGain = new JComboBox( E4KLNAGain.values() ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); + mController.setLNAGain( lnaGain, true ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply LNA gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboLNAGain.setEnabled( false ); + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); + mComboEnhanceGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + E4KEnhanceGain enhance = (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); + mController.setEnhanceGain( enhance, true ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( E4KTunerEditor.this, "E4K Tuner Controller - " + + "couldn't apply the enhance gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "E4K Tuner Controller - couldn't apply enhance gain setting", e ); + } + } + } ); + mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + mComboEnhanceGain.setEnabled( false ); + add( new JLabel( "Enhance:" ) ); + add( mComboEnhanceGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboEnhanceGain.isEnabled() != enabled ) + { + mComboEnhanceGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + Descriptor descriptor = mController.getDescriptor(); + + sb.append( "

    RTL-2832 with E4000 Tuner

    " ); + + if( descriptor == null ) + { + sb.append( "No EEPROM Descriptor Available" ); + } + else + { + sb.append( "USB ID: " ); + sb.append( descriptor.getVendorID() ); + sb.append( ":" ); + sb.append( descriptor.getProductID() ); + sb.append( "
    " ); + + sb.append( "Vendor: " ); + sb.append( descriptor.getVendorLabel() ); + sb.append( "
    " ); + + sb.append( "Product: " ); + sb.append( descriptor.getProductLabel() ); + sb.append( "
    " ); + + sb.append( "Serial: " ); + sb.append( descriptor.getSerial() ); + sb.append( "
    " ); + + sb.append( "IR Enabled: " ); + sb.append( descriptor.irEnabled() ); + sb.append( "
    " ); + + sb.append( "Remote Wake: " ); + sb.append( descriptor.remoteWakeupEnabled() ); + sb.append( "
    " ); + } + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + E4KTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setMasterGain( (E4KGain)mComboMasterGain.getSelectedItem() ); + config.setMixerGain( (E4KMixerGain)mComboMixerGain.getSelectedItem() ); + config.setLNAGain( (E4KLNAGain)mComboLNAGain.getSelectedItem() ); + config.setEnhanceGain( (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem() ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( + getConfiguration(), TunerConfigurationEvent.Event.CHANGE ) ); + } + + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + if( hasItem() ) + { + E4KTunerConfiguration config = getConfiguration(); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboEnhanceGain.setSelectedItem( config.getEnhanceGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + + mLoading = false; + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + } +} diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java b/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java deleted file mode 100644 index fe797d55..00000000 --- a/src/source/tuner/rtl/e4k/E4KTunerEditorPanel.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.rtl.e4k; - -import gui.control.JFrequencyControl; - -import java.awt.Color; -import java.awt.EventQueue; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.IFrequencyChangeProcessor; -import source.tuner.rtl.RTL2832InfoPanel; - -import com.jidesoft.swing.JideTabbedPane; - -public class E4KTunerEditorPanel extends JPanel implements IFrequencyChangeProcessor -{ - private final static Logger mLog = - LoggerFactory.getLogger( E4KTunerEditorPanel.class ); - - private static final long serialVersionUID = 1L; - - private E4KTunerController mController; - private SettingsManager mSettingsManager; - - private JFrequencyControl mFrequencyControl; - - public E4KTunerEditorPanel( E4KTunerController controller, - SettingsManager settingsManager ) - { - mController = controller; - mSettingsManager = settingsManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][grow]", "[][][grow]" ) ); - - add( new JLabel( "RTL2832 with E4000 Tuner" ), "span 2, align center, wrap" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as listener on the controller, to maintain - * sync with the tuned frequency value */ - mController.addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span 2, align center, wrap" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.addTab( "Configuration", - new E4KTunerConfigurationPanel( mSettingsManager, mController ) ); - - tabs.addTab( "Info", new RTL2832InfoPanel( mController ) ); - - add( tabs, "grow,push,span" ); - } - - /** - * Frequency change listener for the frequency control gui component to - * apply end-user requested frequency changes against the tuner - */ - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - final long frequency = event.getValue().longValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequency( frequency ); - } - catch ( SourceException e ) - { - mLog.error( "E4KTunerController - error setting frequency [" + - frequency + "]", e ); - } - } - } ); - } - } -} diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index 2a2e68d8..6fddb93e 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -44,8 +44,6 @@ import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; import source.SourceException; -import source.tuner.airspy.AirspyDeviceInformation; -import source.tuner.airspy.AirspyTunerEditor; import source.tuner.configuration.TunerConfiguration; import source.tuner.configuration.TunerConfigurationEditor; import source.tuner.configuration.TunerConfigurationEvent; @@ -87,121 +85,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor init(); } - - @Override - public void save() - { - if( hasItem() && !mLoading ) - { - R820TTunerConfiguration config = getConfiguration(); - - config.setName( mConfigurationName.getText() ); - - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - config.setFrequencyCorrection( value ); - - config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); - - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - config.setMasterGain( gain ); - - R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); - config.setMixerGain( mixerGain ); - - R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); - config.setLNAGain( lnaGain ); - - R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); - config.setVGAGain( vgaGain ); - - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); - } - } - - @Override - public void setItem( TunerConfiguration item ) - { - super.setItem( item ); - - if( hasItem() ) - { - R820TTunerConfiguration config = getConfiguration(); - - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - - if( item.isAssigned() ) - { - setControlsEnabled( true ); - mConfigurationName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - mComboSampleRate.setSelectedItem( config.getSampleRate() ); - mComboMasterGain.setSelectedItem( config.getMasterGain() ); - mComboMixerGain.setSelectedItem( config.getMixerGain() ); - mComboLNAGain.setSelectedItem( config.getLNAGain() ); - mComboVGAGain.setSelectedItem( config.getVGAGain() ); - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( config.getName() ); - } - - mLoading = false; - } - else - { - setControlsEnabled( false ); - mConfigurationName.setText( "" ); - } - } - /** - * Sets each of the tuner configuration controls to the enabled argument state - */ - private void setControlsEnabled( boolean enabled ) - { - if( mConfigurationName.isEnabled() != enabled ) - { - mConfigurationName.setEnabled( enabled ); - } - - if( mTunerInfo.isEnabled() != enabled ) - { - mTunerInfo.setEnabled( enabled ); - } - - if( mFrequencyCorrection.isEnabled() != enabled ) - { - mFrequencyCorrection.setEnabled( enabled ); - } - - if( mComboSampleRate.isEnabled() != enabled ) - { - mComboSampleRate.setEnabled( enabled ); - } - - if( mComboMasterGain.isEnabled() != enabled ) - { - mComboMasterGain.setEnabled( enabled ); - } - - if( mComboMixerGain.isEnabled() != enabled ) - { - mComboMixerGain.setEnabled( enabled ); - } - - if( mComboLNAGain.isEnabled() != enabled ) - { - mComboLNAGain.setEnabled( enabled ); - } - - if( mComboVGAGain.isEnabled() != enabled ) - { - mComboVGAGain.setEnabled( enabled ); - } - } private R820TTunerConfiguration getConfiguration() { @@ -252,7 +135,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor } ); add( mTunerInfo ); - mComboSampleRate = new JComboBox<>( SampleRate.values() ); mComboSampleRate.setEnabled( false ); mComboSampleRate.addActionListener( new ActionListener() @@ -333,7 +215,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor /** * Gain Controls */ - add( new JLabel( "Gain" ), "span" ); + add( new JLabel( "Gain" ), "span,align left" ); /* Master Gain Control */ mComboMasterGain = new JComboBox( R820TGain.values() ); @@ -520,6 +402,52 @@ public class R820TTunerEditor extends TunerConfigurationEditor add( mComboVGAGain ); } + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mComboMasterGain.isEnabled() != enabled ) + { + mComboMasterGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboVGAGain.isEnabled() != enabled ) + { + mComboVGAGain.setEnabled( enabled ); + } + } + private String getTunerInfo() { StringBuilder sb = new StringBuilder(); @@ -563,4 +491,74 @@ public class R820TTunerEditor extends TunerConfigurationEditor return sb.toString(); } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + R820TTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setSampleRate( (SampleRate)mComboSampleRate.getSelectedItem() ); + + R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); + config.setMasterGain( gain ); + + R820TMixerGain mixerGain = (R820TMixerGain)mComboMixerGain.getSelectedItem(); + config.setMixerGain( mixerGain ); + + R820TLNAGain lnaGain = (R820TLNAGain)mComboLNAGain.getSelectedItem(); + config.setLNAGain( lnaGain ); + + R820TVGAGain vgaGain = (R820TVGAGain)mComboVGAGain.getSelectedItem(); + config.setVGAGain( vgaGain ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + if( hasItem() ) + { + R820TTunerConfiguration config = getConfiguration(); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mComboMasterGain.setSelectedItem( config.getMasterGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + + mLoading = false; + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + } } From 7a5dc5ad58d98852afe158be8427244191b61fcb Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Mar 2016 06:27:53 -0400 Subject: [PATCH 64/74] wip: #34 Updates the HackRF tuner editor and a few other tuner config/editor items --- .../TunerConfigurationFactory.java | 4 +- .../tuner/hackrf/HackRFInformationPanel.java | 104 --- .../hackrf/HackRFTunerConfigurationPanel.java | 557 -------------- .../tuner/hackrf/HackRFTunerEditor.java | 464 ++++++++++++ .../tuner/hackrf/HackRFTunerEditorPanel.java | 123 ---- src/source/tuner/rtl/RTL2832InfoPanel.java | 76 -- src/source/tuner/rtl/RTL2832TunerType.java | 28 - .../rtl/e4k/E4KTunerConfigurationPanel.java | 687 ----------------- src/source/tuner/rtl/e4k/E4KTunerEditor.java | 9 +- .../R820TTunerConfigurationEditorOld.java | 695 ------------------ .../tuner/rtl/r820t/R820TTunerEditor.java | 13 +- 11 files changed, 478 insertions(+), 2282 deletions(-) delete mode 100644 src/source/tuner/hackrf/HackRFInformationPanel.java delete mode 100644 src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java create mode 100644 src/source/tuner/hackrf/HackRFTunerEditor.java delete mode 100644 src/source/tuner/hackrf/HackRFTunerEditorPanel.java delete mode 100644 src/source/tuner/rtl/RTL2832InfoPanel.java delete mode 100644 src/source/tuner/rtl/RTL2832TunerType.java delete mode 100644 src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java delete mode 100644 src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java index 7684f820..1851bec7 100644 --- a/src/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -9,7 +9,9 @@ import source.tuner.airspy.AirspyTunerConfiguration; import source.tuner.airspy.AirspyTunerEditor; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; +import source.tuner.hackrf.HackRFTuner; import source.tuner.hackrf.HackRFTunerConfiguration; +import source.tuner.hackrf.HackRFTunerEditor; import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.e4k.E4KTunerConfiguration; import source.tuner.rtl.e4k.E4KTunerEditor; @@ -62,7 +64,7 @@ public class TunerConfigurationFactory case FUNCUBE_DONGLE_PRO_PLUS: return new EmptyEditor( "a tuner" ); case HACKRF: - return new EmptyEditor( "a tuner" ); + return new HackRFTunerEditor( model, (HackRFTuner)tuner ); case RAFAELMICRO_R820T: return new R820TTunerEditor( model, (RTL2832Tuner)tuner ); case UNKNOWN: diff --git a/src/source/tuner/hackrf/HackRFInformationPanel.java b/src/source/tuner/hackrf/HackRFInformationPanel.java deleted file mode 100644 index f3825577..00000000 --- a/src/source/tuner/hackrf/HackRFInformationPanel.java +++ /dev/null @@ -1,104 +0,0 @@ -package source.tuner.hackrf; - -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.tuner.hackrf.HackRFTunerController.Serial; - -public class HackRFInformationPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = - LoggerFactory.getLogger( HackRFInformationPanel.class ); - - private HackRFTunerController mController; - - /** - * Displays HackRF firmware, serial number and part identifier - */ - public HackRFInformationPanel( HackRFTunerController controller ) - { - mController = controller; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", - "[right,grow][grow]", - "[][][][grow]" ) ); - - Serial serial = null; - - try - { - serial = mController.getSerial(); - } - catch( Exception e ) - { - mLog.error( "couldn't read HackRF serial number", e ); - } - - if( serial != null ) - { - add( new JLabel( "Serial:" ) ); - add( new JLabel( serial.getSerialNumber() ) ); - add( new JLabel( "Part:" ) ); - add( new JLabel( serial.getPartID() ) ); - } - else - { - add( new JLabel( "Serial:" ) ); - add( new JLabel( "Unknown" ) ); - add( new JLabel( "Part:" ) ); - add( new JLabel( "Unknown" ) ); - } - - String firmware = null; - - try - { - firmware = mController.getFirmwareVersion(); - } - catch( Exception e ) - { - mLog.error( "couldn't read HackRF firmware version", e ); - } - - add( new JLabel( "Firmware:" ) ); - - if( firmware != null ) - { - add( new JLabel( firmware ) ); - } - else - { - add( new JLabel( "Unknown" ) ); - } - } -} diff --git a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java deleted file mode 100644 index 92ff881b..00000000 --- a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java +++ /dev/null @@ -1,557 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.hackrf; - -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.BorderFactory; -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.JToggleButton; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; -import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; -import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; - -public class HackRFTunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( HackRFTunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - - private SettingsManager mSettingsManager; - private HackRFTunerController mController; - private HackRFTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JSpinner mFrequencyCorrection; - private JToggleButton mAmplifier; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - - private JComboBox mComboSampleRate; - - public HackRFTunerConfigurationPanel( SettingsManager settingsManager, - HackRFTunerController controller ) - { - mSettingsManager = settingsManager; - mController = controller; - - init(); - } - - /** - * Initializes the gui components - */ - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[grow,right][grow]", "[][][][][][][grow]" ) ); - - /** - * Tuner configuration selector - */ - mComboConfigurations = new JComboBox(); - - mComboConfigurations.setModel( getModel() ); - - /* Determine which tuner configuration should be selected/displayed */ -// TunerConfigurationAssignment savedConfig = null; - String serial = null; - - try - { - serial = mController.getSerial().getSerialNumber(); - } - catch ( UsbException e2 ) - { - mLog.error( "couldn't read hackrf serial number", e2 ); - } - -// if( serial != null ) -// { -// savedConfig = mSettingsManager.getSelectedTunerConfiguration( -// TunerType.HACKRF, serial ); -// } -// -// if( savedConfig != null ) -// { -// mSelectedConfig = getNamedConfiguration( -// savedConfig.getTunerConfigurationName() ); -// } - - /* If we couldn't determine the saved/selected config, use the first one */ - if( mSelectedConfig == null ) - { - mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - - //Store this config as the default for this tuner at this address - -// if( serial != null ) -// { -// mSettingsManager -// .setSelectedTunerConfiguration( TunerType.HACKRF, -// serial, mSelectedConfig ); -// } - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - HackRFTunerConfiguration selected = - (HackRFTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - - /** - * Tuner Configuration Name - */ - mName = new JTextField(); - mName.setText( mSelectedConfig.getName() ); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - mSelectedConfig.setName( mName.getText() ); - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mName, "growx,push" ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.setValue( mSelectedConfig.getFrequencyCorrection() ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequencyCorrection( value ); - mSelectedConfig.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "HackRF Tuner Controller - couldn't apply " - + "frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * Sample Rate - */ - mComboSampleRate = new JComboBox<>( HackRFSampleRate.values() ); - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - HackRFSampleRate sampleRate = - (HackRFSampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - - mSelectedConfig.setSampleRate( sampleRate ); - save(); - } - catch ( UsbException e2 ) - { - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "HackRF Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - e2.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", e ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate, "growx,push" ); - - /** - * Gain Controls - */ - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "", - "[][right][grow,fill][right][grow,fill]", "[grow,fill]" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - - /* Amplifier */ - mAmplifier = new JToggleButton( "Amp" ); - mAmplifier.setSelected( mSelectedConfig.getAmplifierEnabled() ); - mAmplifier.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - mController.setAmplifierEnabled( mAmplifier.isSelected() ); - mSelectedConfig.setAmplifierEnabled( mAmplifier.isSelected() ); - save(); - } - catch ( UsbException e ) - { - mLog.error( "couldn't enable/disable amplifier", e ); - mAmplifier.setEnabled( !mAmplifier.isSelected() ); - - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "Couldn't change amplifier setting", - "Error changing amplifier setting", - JOptionPane.ERROR_MESSAGE ); - } - } - } ); - gainPanel.add( mAmplifier ); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( HackRFLNAGain.values() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - HackRFLNAGain lnaGain = - (HackRFLNAGain)mComboLNAGain.getSelectedItem(); - - if ( lnaGain == null ) - { - lnaGain = HackRFLNAGain.GAIN_0; - } - - if( mComboLNAGain.isEnabled() ) - { - mController.setLNAGain( lnaGain ); - } - - mSelectedConfig.setLNAGain( lnaGain ); - - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "HackRF Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Adjust to set " - + "the IF gain" ); - gainPanel.add( new JLabel( "LNA" ) ); - gainPanel.add( mComboLNAGain ); - - /* VGA Gain Control */ - mComboVGAGain = new JComboBox( HackRFVGAGain.values() ); - mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); - mComboVGAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); - - if( vgaGain == null ) - { - vgaGain = HackRFVGAGain.GAIN_4; - } - - if( mComboVGAGain.isEnabled() ) - { - mController.setVGAGain( vgaGain ); - } - - mSelectedConfig.setVGAGain( vgaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "HackRF Tuner Controller - couldn't apply the VGA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply VGA " - + "gain setting", e ); - } - } - } ); - mComboVGAGain.setToolTipText( "VGA Gain. Adjust to set the " - + "baseband gain" ); - gainPanel.add( new JLabel( "VGA" ) ); - gainPanel.add( mComboVGAGain, "wrap" ); - - add( gainPanel, "span,growx" ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { -// TunerConfiguration config = -// mSettingsManager -// .addNewTunerConfiguration( -// TunerType.HACKRF, -// "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - -// mComboConfigurations.setSelectedItem( config ); - - repaint(); - } - } ); - - add( mNewConfiguration, "growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - HackRFTunerConfiguration selected = - (HackRFTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - HackRFTunerConfigurationPanel.this, - "Are you sure you want to delete '" - + selected.getName() + "'?", - "Are you sure?", - JOptionPane.YES_NO_OPTION ); - - if( n == JOptionPane.YES_OPTION ) - { -// mSettingsManager -// .deleteTunerConfiguration( selected ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedIndex( 0 ); - - repaint(); - } - } - } - } ); - - add( mDeleteConfiguration, "growx,push,wrap" ); - } - - /** - * Updates gui controls with the values from the tuner configuration - * @param config - tuner configuration - */ - private void update( HackRFTunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); - - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - -// mSettingsManager.setSelectedTunerConfiguration( -// TunerType.HACKRF, mController.getSerial().getSerialNumber(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - HackRFTunerConfigurationPanel.this, - "HackRF Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "HackRF Tuner Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - /** - * Constructs a combo box model for the tuner configuration combo component - * by assembling a list of the tuner configurations appropriate for this - * specific tuner - */ - private ComboBoxModel getModel() - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.HACKRF ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - -// for( TunerConfiguration config: configs ) -// { -// model.addElement( (HackRFTunerConfiguration)config ); -// } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private HackRFTunerConfiguration getNamedConfiguration( String name ) - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.HACKRF ); - -// for( TunerConfiguration config: configs ) -// { -// if( config.getName().contentEquals( name ) ) -// { -// return (HackRFTunerConfiguration)config; -// } -// } - - return null; - } - - /** - * Saves current tuner configuration settings - */ - private void save() - { -// mSettingsManager.save(); - } -} diff --git a/src/source/tuner/hackrf/HackRFTunerEditor.java b/src/source/tuner/hackrf/HackRFTunerEditor.java new file mode 100644 index 00000000..81542ef5 --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTunerEditor.java @@ -0,0 +1,464 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source.tuner.hackrf; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.JToggleButton; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.usb.UsbException; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceException; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationEvent.Event; +import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.hackrf.HackRFTunerController.BoardID; +import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; +import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; +import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; +import source.tuner.hackrf.HackRFTunerController.Serial; + +public class HackRFTunerEditor extends TunerConfigurationEditor +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = LoggerFactory.getLogger( HackRFTunerEditor.class ); + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JComboBox mComboSampleRate; + private JSpinner mFrequencyCorrection; + private JToggleButton mAmplifier; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; + private boolean mLoading; + + private HackRFTunerController mController; + + public HackRFTunerEditor( TunerConfigurationModel tunerConfigurationModel, HackRFTuner tuner ) + { + super( tunerConfigurationModel ); + + mController = tuner.getController(); + + init(); + } + + private HackRFTunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (HackRFTunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][grow]" ) ); + + add( new JLabel( "HackRF Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + mComboSampleRate = new JComboBox<>( HackRFSampleRate.values() ); + mComboSampleRate.setEnabled( false ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + HackRFSampleRate sampleRate = + (HackRFSampleRate)mComboSampleRate.getSelectedItem(); + try + { + + mController.setSampleRate( sampleRate ); + save(); + } + catch ( UsbException e2 ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the sample rate setting [" + sampleRate.getLabel() + + "] " + e2.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply sample rate setting [" + + sampleRate.getLabel() + "]", e ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate ); + + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply frequency correction " + + "value: " + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ) ); + add( new JLabel( "" ), "span 2" ); //filler + + mAmplifier = new JToggleButton( "Amplifier" ); + mAmplifier.setEnabled( false ); + mAmplifier.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + mController.setAmplifierEnabled( mAmplifier.isSelected() ); + save(); + } + catch ( UsbException e ) + { + mLog.error( "couldn't enable/disable amplifier", e ); + + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "Couldn't change amplifier setting", + "Error changing amplifier setting", JOptionPane.ERROR_MESSAGE ); + } + } + } ); + add( mAmplifier ); + + mComboLNAGain = new JComboBox( HackRFLNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + HackRFLNAGain lnaGain = (HackRFLNAGain)mComboLNAGain.getSelectedItem(); + + if ( lnaGain == null ) + { + lnaGain = HackRFLNAGain.GAIN_0; + } + + mController.setLNAGain( lnaGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the LNA gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply LNA gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Adjust to set the IF gain" ); + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + mComboVGAGain = new JComboBox( HackRFVGAGain.values() ); + mComboVGAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); + + if( vgaGain == null ) + { + vgaGain = HackRFVGAGain.GAIN_4; + } + + mController.setVGAGain( vgaGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( HackRFTunerEditor.this, "HackRF Tuner Controller" + + " - couldn't apply the VGA gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply VGA gain setting", e ); + } + } + } ); + mComboVGAGain.setToolTipText( "VGA Gain. Adjust to set the baseband gain" ); + add( new JLabel( "VGA:" ) ); + add( mComboVGAGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboSampleRate.isEnabled() != enabled ) + { + mComboSampleRate.setEnabled( enabled ); + } + + if( mAmplifier.isEnabled() != enabled ) + { + mAmplifier.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboVGAGain.isEnabled() != enabled ) + { + mComboVGAGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + BoardID board = BoardID.INVALID; + + try + { + board = mController.getBoardID(); + } + catch ( UsbException e ) + { + mLog.error( "couldn't read HackRF board identifier", e ); + } + + StringBuilder sb = new StringBuilder(); + + sb.append( "

    HackRF Tuner

    " ); + + sb.append( "Board: " ); + sb.append( board.getLabel() ); + sb.append( "
    " ); + + Serial serial = null; + + try + { + serial = mController.getSerial(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF serial number", e ); + } + + if( serial != null ) + { + sb.append( "Serial: " ); + sb.append( serial.getSerialNumber() ); + sb.append( "
    " ); + + sb.append( "Part: " ); + sb.append( serial.getPartID() ); + sb.append( "
    " ); + } + else + { + sb.append( "Serial: Unknown
    " ); + sb.append( "Part: Unknown
    " ); + } + + String firmware = null; + + try + { + firmware = mController.getFirmwareVersion(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF firmware version", e ); + } + + if( firmware != null ) + { + sb.append( "Firmware: " ); + sb.append( firmware ); + sb.append( "
    " ); + } + else + { + sb.append( "Firmware: Unknown
    " ); + } + + return sb.toString(); + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + if( hasItem() ) + { + HackRFTunerConfiguration config = getConfiguration(); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + + mConfigurationName.setText( config.getName() ); + mComboSampleRate.setSelectedItem( config.getSampleRate() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mAmplifier.setSelected( config.getAmplifierEnabled() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboVGAGain.setSelectedItem( config.getVGAGain() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + + mLoading = false; + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + HackRFTunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + config.setSampleRate( (HackRFSampleRate)mComboSampleRate.getSelectedItem() ); + config.setAmplifierEnabled( mAmplifier.isSelected() ); + config.setLNAGain( (HackRFLNAGain)mComboLNAGain.getSelectedItem() ); + config.setVGAGain( (HackRFVGAGain)mComboVGAGain.getSelectedItem() ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } +} diff --git a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java deleted file mode 100644 index c0666bb0..00000000 --- a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java +++ /dev/null @@ -1,123 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.hackrf; - -import gui.control.JFrequencyControl; - -import java.awt.Color; - -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.IFrequencyChangeProcessor; -import source.tuner.hackrf.HackRFTunerController.BoardID; - -import com.jidesoft.swing.JideTabbedPane; - -public class HackRFTunerEditorPanel extends JPanel implements IFrequencyChangeProcessor -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = - LoggerFactory.getLogger( HackRFTunerEditorPanel.class ); - - private JFrequencyControl mFrequencyControl; - - private HackRFTuner mTuner; - private HackRFTunerController mController; - private SettingsManager mSettingsManager; - - public HackRFTunerEditorPanel( HackRFTuner tuner, - SettingsManager resourceManager ) - { - mTuner = tuner; - mController = mTuner.getController(); - mSettingsManager = resourceManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", - "[right,grow][grow]", - "[][][][][][grow]" ) ); - - BoardID board = BoardID.INVALID; - - try - { - board = mTuner.getController().getBoardID(); - } - catch ( UsbException e ) - { - mLog.error( "couldn't read HackRF board identifier", e ); - } - - add( new JLabel( board.getLabel() ), "span,align center" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as frequency change listener. This creates a - * feedback loop, so the control does not rebroadcast the event */ - mTuner.getController().addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span,align center" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - add( tabs, "span,grow,push" ); - - tabs.add( "Config", new HackRFTunerConfigurationPanel( - mSettingsManager, mTuner.getController() ) ); - - tabs.add( "Info", new HackRFInformationPanel( mTuner.getController() ) ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "error setting frequency [" + event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/rtl/RTL2832InfoPanel.java b/src/source/tuner/rtl/RTL2832InfoPanel.java deleted file mode 100644 index f65bfbc9..00000000 --- a/src/source/tuner/rtl/RTL2832InfoPanel.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.rtl; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; -import source.tuner.rtl.RTL2832TunerController.Descriptor; - -public class RTL2832InfoPanel extends JPanel -{ - RTL2832TunerController mController; - - public RTL2832InfoPanel( RTL2832TunerController controller ) - { - mController = controller; - - init(); - } - - public void dispose() - { - mController = null; - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][][][grow]" ) ); - - Descriptor descriptor = mController.getDescriptor(); - - add( new JLabel( "RTL-2832 EEPROM Descriptor" ), "span,align center" ); - - if( descriptor == null ) - { - add( new JLabel( "No descriptor" ), "wrap" ) ; - } - else - { - add( new JLabel( "USB ID:" ) ) ; - add( new JLabel( descriptor.getVendorID() + ":" + - descriptor.getProductID() ), "wrap" ) ; - - add( new JLabel( "Vendor:" ) ) ; - add( new JLabel( descriptor.getVendorLabel() ), "wrap" ) ; - - add( new JLabel( "Product:" ) ) ; - add( new JLabel( descriptor.getProductLabel() ), "wrap" ) ; - - add( new JLabel( "Serial:" ) ) ; - add( new JLabel( descriptor.getSerial() ), "wrap" ) ; - - add( new JLabel( "IR Enabled:" ) ) ; - add( new JLabel( ( descriptor.irEnabled() ? "Yes" : "No" ) ), "wrap" ) ; - - add( new JLabel( "Remote Wake:" ) ) ; - add( new JLabel( ( descriptor.remoteWakeupEnabled() ? "Yes" : "No" ) ), "wrap" ) ; - } - } -} diff --git a/src/source/tuner/rtl/RTL2832TunerType.java b/src/source/tuner/rtl/RTL2832TunerType.java deleted file mode 100644 index 72233f32..00000000 --- a/src/source/tuner/rtl/RTL2832TunerType.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.rtl; - -public enum RTL2832TunerType -{ - E4000, - FC0012, - FC0013, - R820T, - R828D, - UNKNOWN; -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java b/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java deleted file mode 100644 index 3ec3e0db..00000000 --- a/src/source/tuner/rtl/e4k/E4KTunerConfigurationPanel.java +++ /dev/null @@ -1,687 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.rtl.e4k; - -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.BorderFactory; -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.rtl.RTL2832TunerController.SampleRate; -import source.tuner.rtl.e4k.E4KTunerController.E4KEnhanceGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KLNAGain; -import source.tuner.rtl.e4k.E4KTunerController.E4KMixerGain; - -public class E4KTunerConfigurationPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( E4KTunerConfigurationPanel.class ); - - private SettingsManager mSettingsManager; - private E4KTunerController mController; - private E4KTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JSpinner mFrequencyCorrection; - private JSpinner mSampleRateCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboEnhanceGain; - - private JComboBox mComboSampleRate; - - public E4KTunerConfigurationPanel( SettingsManager settingsManager, - E4KTunerController controller ) - { - mSettingsManager = settingsManager; - mController = controller; - - init(); - } - - /** - * Initializes the gui components - */ - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[grow,right][grow]", "[][][][][][][grow]" ) ); - - /** - * Tuner configuration selector - */ - mComboConfigurations = new JComboBox(); - - mComboConfigurations.setModel( getModel() ); - - /* Determine which tuner configuration should be selected/displayed */ -// TunerConfigurationAssignment savedConfig = mSettingsManager -// .getSelectedTunerConfiguration( -// TunerType.ELONICS_E4000, mController.getUniqueID() ); -// -// if( savedConfig != null ) -// { -// mSelectedConfig = getNamedConfiguration( -// savedConfig.getTunerConfigurationName() ); -// } - - /* If we couldn't determine the saved/selected config, use the first one */ - if( mSelectedConfig == null ) - { - mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - - //Store this config as the default for this tuner at this address -// mSettingsManager -// .setSelectedTunerConfiguration( -// TunerType.ELONICS_E4000, -// mController.getUniqueID(), mSelectedConfig ); - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - E4KTunerConfiguration selected = - (E4KTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - /** - * Tuner Configuration Name - */ - add( new JLabel( "Name:" ) ); - mName = new JTextField(); - mName.setText( mSelectedConfig.getName() ); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - /* Update the config name */ - mSelectedConfig.setName( mName.getText() ); - - /* Reset this named config as the assigned config for this - * tuner */ -// mSettingsManager -// .setSelectedTunerConfiguration( -// TunerType.ELONICS_E4000, -// mController.getUniqueID(), mSelectedConfig ); - - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( mName, "growx,push" ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.setValue( mSelectedConfig.getFrequencyCorrection() ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequencyCorrection( value ); - mSelectedConfig.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply " - + "frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * Sample Rate - */ - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - - mSelectedConfig.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate, "growx,push" ); - -// /** -// * Sample Rate Correction -// */ -// SpinnerModel sampleRateCorrectionModel = -// new SpinnerNumberModel( 0, //initial value -// -32768, //min -// 32767, //max -// 1 ); //step -// -// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); -// -// JSpinner.NumberEditor sampleRateCorrectionEditor = -// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); -// -// DecimalFormat sampleRateCorrectionFormat = -// sampleRateCorrectionEditor.getFormat(); -// -// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); -// -// sampleRateCorrectionEditor.getTextField() -// .setHorizontalAlignment( SwingConstants.CENTER ); -// -// mSampleRateCorrection.addChangeListener( new ChangeListener() -// { -// @Override -// public void stateChanged( ChangeEvent e ) -// { -// final int value = ((SpinnerNumberModel)mSampleRateCorrection -// .getModel()).getNumber().intValue(); -// -// EventQueue.invokeLater( new Runnable() -// { -// @Override -// public void run() -// { -// try -// { -// mController.setSampleRateFrequencyCorrection( value ); -// } -// catch ( SourceException | UsbException e1 ) -// { -// JOptionPane.showMessageDialog( -// E4KTunerConfigurationPanel.this, -// "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// -// Log.error( "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// } -// } -// } ); -// } -// } ); -// -// add( mSampleRateCorrection ); -// add( new JLabel( "Sample Rate Correction (ppm)" ), "wrap" ); - - /** - * Gain Controls - */ - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "fill", "[right][left][right][left]", "" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox( E4KGain.values() ); - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboMasterGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KGain gain = (E4KGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (E4KGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == E4KGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( - mController.getMixerGain( true ) ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( - mController.getLNAGain( true ) ); - mComboLNAGain.setEnabled( true ); - - mComboEnhanceGain.setEnabled( true ); - mComboEnhanceGain.setSelectedItem( - mController.getEnhanceGain( true ) ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - - mComboEnhanceGain.setEnabled( false ); - mComboEnhanceGain.setSelectedItem( gain.getEnhanceGain() ); - } - - mSelectedConfig.setMasterGain( gain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "gain setting", e ); - } - } - } ); - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
    independent control of " - + "Mixer, LNA and Enhance gain
    " - + "settings, or one of the individual gain settings for
    " - + "semi-manual gain control" ); - gainPanel.add( new JLabel( "Master" ) ); - gainPanel.add( mComboMasterGain ); - - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox( E4KMixerGain.values() ); - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - E4KMixerGain mixerGain = - (E4KMixerGain)mComboMixerGain.getSelectedItem(); - - if( mComboMixerGain.isEnabled() ) - { - mController.setMixerGain( mixerGain, true ); - } - - mSelectedConfig.setMixerGain( mixerGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply mixer " - + "gain setting", e ); - } - } - } ); - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); - gainPanel.add( new JLabel( "Mixer" ) ); - gainPanel.add( mComboMixerGain, "wrap" ); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( E4KLNAGain.values() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KLNAGain lnaGain = (E4KLNAGain)mComboLNAGain.getSelectedItem(); - - if( mComboLNAGain.isEnabled() ) - { - mController.setLNAGain( lnaGain, true ); - } - - mSelectedConfig.setLNAGain( lnaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); - gainPanel.add( new JLabel( "LNA" ) ); - gainPanel.add( mComboLNAGain ); - - /* Enhance Gain Control */ - mComboEnhanceGain = new JComboBox( E4KEnhanceGain.values() ); - mComboEnhanceGain.setSelectedItem( mSelectedConfig.getEnhanceGain() ); - mComboEnhanceGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - E4KEnhanceGain enhanceGain = - (E4KEnhanceGain)mComboEnhanceGain.getSelectedItem(); - - if( mComboEnhanceGain.isEnabled() ) - { - mController.setEnhanceGain( enhanceGain, true ); - } - - mSelectedConfig.setEnhanceGain( enhanceGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't apply the enhance " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply enhance " - + "gain setting", e ); - } - } - } ); - mComboEnhanceGain.setToolTipText( "Enhance Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboEnhanceGain.setEnabled( false ); - gainPanel.add( new JLabel( "Enhance" ) ); - gainPanel.add( mComboEnhanceGain, "wrap" ); - - add( gainPanel, "span,grow,push" ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { -// TunerConfiguration config = -// mSettingsManager -// .addNewTunerConfiguration( -// TunerType.ELONICS_E4000, -// "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - -// mComboConfigurations.setSelectedItem( config ); - - repaint(); - } - } ); - - add( mNewConfiguration, "growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - E4KTunerConfiguration selected = - (E4KTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - E4KTunerConfigurationPanel.this, - "Are you sure you want to delete '" - + selected.getName() + "'?", - "Are you sure?", - JOptionPane.YES_NO_OPTION ); - - if( n == JOptionPane.YES_OPTION ) - { -// mSettingsManager -// .deleteTunerConfiguration( selected ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedIndex( 0 ); - - repaint(); - } - } - } - } ); - - add( mDeleteConfiguration, "growx,push,wrap" ); - } - - /** - * Updates gui controls with the values from the tuner configuration - * @param config - tuner configuration - */ - private void update( E4KTunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboEnhanceGain.setSelectedItem( mSelectedConfig.getEnhanceGain() ); - - /* Apply master gain last so that the Mixer, LNA, and Enhance gain - * settings are updated with the master setting where necessary */ - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - -// mSettingsManager.setSelectedTunerConfiguration( -// TunerType.ELONICS_E4000, mController.getUniqueID(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - E4KTunerConfigurationPanel.this, - "E4K Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "E4K Tuner Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - /** - * Constructs a combo box model for the tuner configuration combo component - * by assembling a list of the tuner configurations appropriate for this - * specific tuner - */ - private ComboBoxModel getModel() - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.ELONICS_E4000 ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - -// for( TunerConfiguration config: configs ) -// { -// model.addElement( (E4KTunerConfiguration)config ); -// } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private E4KTunerConfiguration getNamedConfiguration( String name ) - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.ELONICS_E4000 ); -// -// for( TunerConfiguration config: configs ) -// { -// if( config.getName().contentEquals( name ) ) -// { -// return (E4KTunerConfiguration)config; -// } -// } - - return null; - } - - /** - * Saves current tuner configuration settings - */ - private void save() - { -// mSettingsManager.save(); - } -} diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/source/tuner/rtl/e4k/E4KTunerEditor.java index 67cfb310..1ea51265 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerEditor.java +++ b/src/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -27,6 +27,7 @@ import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; +import javax.swing.JSeparator; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerModel; @@ -131,6 +132,7 @@ public class E4KTunerEditor extends TunerConfigurationEditor add( mTunerInfo ); mComboSampleRate = new JComboBox<>( SampleRate.values() ); + mComboSampleRate.setEnabled( false ); mComboSampleRate.addActionListener( new ActionListener() { @Override @@ -199,6 +201,9 @@ public class E4KTunerEditor extends TunerConfigurationEditor add( new JLabel( "PPM:" ) ); add( mFrequencyCorrection ); + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ), "wrap" ); + mComboMasterGain = new JComboBox( E4KGain.values() ); mComboMasterGain.setEnabled( false ); mComboMasterGain.addActionListener( new ActionListener() @@ -252,7 +257,7 @@ public class E4KTunerEditor extends TunerConfigurationEditor + "Mixer, LNA and Enhance gain
    " + "settings, or one of the individual gain settings for
    " + "semi-manual gain control" ); - add( new JLabel( "Master" ) ); + add( new JLabel( "Master:" ) ); add( mComboMasterGain ); mComboMixerGain = new JComboBox( E4KMixerGain.values() ); @@ -279,7 +284,7 @@ public class E4KTunerEditor extends TunerConfigurationEditor mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " + "to MASTER to enable adjustment" ); mComboMixerGain.setEnabled( false ); - add( new JLabel( "Mixer" ) ); + add( new JLabel( "Mixer:" ) ); add( mComboMixerGain ); mComboLNAGain = new JComboBox( E4KLNAGain.values() ); diff --git a/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java b/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java deleted file mode 100644 index 841a8e49..00000000 --- a/src/source/tuner/rtl/r820t/R820TTunerConfigurationEditorOld.java +++ /dev/null @@ -1,695 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.rtl.r820t; - -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.BorderFactory; -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.usb4java.LibUsbException; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.rtl.RTL2832TunerController.SampleRate; -import source.tuner.rtl.r820t.R820TTunerController.R820TGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TMixerGain; -import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; - -public class R820TTunerConfigurationEditorOld extends JPanel -{ - private static final long serialVersionUID = 1L; - - private final static Logger mLog = - LoggerFactory.getLogger( R820TTunerConfigurationEditorOld.class ); - - private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; - - private SettingsManager mSettingsManager; - private R820TTunerController mController; - private R820TTunerConfiguration mSelectedConfig; - - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - - private JTextField mName; - - private JSpinner mFrequencyCorrection; - private JSpinner mSampleRateCorrection; - private JComboBox mComboMasterGain; - private JComboBox mComboMixerGain; - private JComboBox mComboLNAGain; - private JComboBox mComboVGAGain; - - private JComboBox mComboSampleRate; - - public R820TTunerConfigurationEditorOld( SettingsManager settingsManager, - R820TTunerController controller ) - { - mSettingsManager = settingsManager; - mController = controller; - - init(); - } - - /** - * Initializes the gui components - */ - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[grow,right][grow]", "[][][][][][][grow]" ) ); - - /** - * Tuner configuration selector - */ - mComboConfigurations = new JComboBox(); - - mComboConfigurations.setModel( getModel() ); - - /* Determine which tuner configuration should be selected/displayed */ -// TunerConfigurationAssignment savedConfig = mSettingsManager -// .getSelectedTunerConfiguration( -// TunerType.RAFAELMICRO_R820T, mController.getUniqueID() ); -// -// if( savedConfig != null ) -// { -// mSelectedConfig = getNamedConfiguration( -// savedConfig.getTunerConfigurationName() ); -// } - - /* If we couldn't determine the saved/selected config, use the first one */ -// if( mSelectedConfig == null ) -// { -// mSelectedConfig = mComboConfigurations.getItemAt( 0 ); -// -// //Store this config as the default for this tuner at this address -// mSettingsManager -// .setSelectedTunerConfiguration( -// TunerType.RAFAELMICRO_R820T, -// mController.getUniqueID(), mSelectedConfig ); -// } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - R820TTunerConfiguration selected = - (R820TTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - - /** - * Tuner Configuration Name - */ - mName = new JTextField(); - mName.setText( mSelectedConfig.getName() ); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - mSelectedConfig.setName( mName.getText() ); - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mName, "growx,push" ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.setValue( mSelectedConfig.getFrequencyCorrection() ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - final double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - mController.setFrequencyCorrection( value ); - mSelectedConfig.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't apply " - + "frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * Sample Rate - */ - mComboSampleRate = new JComboBox<>( SampleRate.values() ); - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - mComboSampleRate.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SampleRate sampleRate = - (SampleRate)mComboSampleRate.getSelectedItem(); - try - { - - mController.setSampleRate( sampleRate ); - - mSelectedConfig.setSampleRate( sampleRate ); - save(); - } - catch ( SourceException | LibUsbException eSampleRate ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't apply the sample " - + "rate setting [" + sampleRate.getLabel() + "] " + - eSampleRate.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply sample " - + "rate setting [" + sampleRate.getLabel() + "]", - eSampleRate ); - } - } - } ); - add( new JLabel( "Sample Rate:" ) ); - add( mComboSampleRate, "growx,push" ); - -// /** -// * Sample Rate Correction -// */ -// SpinnerModel sampleRateCorrectionModel = -// new SpinnerNumberModel( 0, //initial value -// -32768, //min -// 32767, //max -// 1 ); //step -// -// mSampleRateCorrection = new JSpinner( sampleRateCorrectionModel ); -// -// JSpinner.NumberEditor sampleRateCorrectionEditor = -// (JSpinner.NumberEditor)mSampleRateCorrection.getEditor(); -// -// DecimalFormat sampleRateCorrectionFormat = -// sampleRateCorrectionEditor.getFormat(); -// -// sampleRateCorrectionFormat.setMinimumFractionDigits( 0 ); -// -// sampleRateCorrectionEditor.getTextField() -// .setHorizontalAlignment( SwingConstants.CENTER ); -// -// mSampleRateCorrection.addChangeListener( new ChangeListener() -// { -// @Override -// public void stateChanged( ChangeEvent e ) -// { -// final int value = ((SpinnerNumberModel)mSampleRateCorrection -// .getModel()).getNumber().intValue(); -// -// EventQueue.invokeLater( new Runnable() -// { -// @Override -// public void run() -// { -// try -// { -// mController.setSampleRateFrequencyCorrection( value ); -// } -// catch ( SourceException | LibUsbException e1 ) -// { -// JOptionPane.showMessageDialog( -// R820TTunerConfigurationPanel.this, -// "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// -// mLog.error( "E4K Tuner Controller - couldn't apply " -// + "sample rate correction value: " + value + -// e1.getLocalizedMessage() ); -// } -// } -// } ); -// } -// } ); -// -// add( mSampleRateCorrection ); -// add( new JLabel( "Sample Rate Correction (ppm)" ), "grow,push" ); - - /** - * Gain Controls - */ - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); - gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); - - /* Master Gain Control */ - mComboMasterGain = new JComboBox( R820TGain.values() ); - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboMasterGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TGain gain = (R820TGain)mComboMasterGain.getSelectedItem(); - - mController.setGain( (R820TGain)mComboMasterGain.getSelectedItem(), true ); - - if( gain == R820TGain.MANUAL ) - { - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - mComboMixerGain.setEnabled( true ); - - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - mComboLNAGain.setEnabled( true ); - - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - mComboVGAGain.setEnabled( true ); - } - else - { - mComboMixerGain.setEnabled( false ); - mComboMixerGain.setSelectedItem( gain.getMixerGain() ); - - mComboLNAGain.setEnabled( false ); - mComboLNAGain.setSelectedItem( gain.getLNAGain() ); - - mComboVGAGain.setEnabled( false ); - mComboVGAGain.setSelectedItem( gain.getVGAGain() ); - } - - mSelectedConfig.setMasterGain( gain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't apply the gain " - + "setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "gain setting - ", e ); - } - } - } ); - mComboMasterGain.setToolTipText( "Select AUTOMATIC for auto " - + "gain, MANUAL to enable
    independent control of " - + "Mixer, LNA and Enhance gain
    " - + "settings, or one of the individual gain settings for
    " - + "semi-manual gain control" ); - gainPanel.add( new JLabel( "Master" ) ); - gainPanel.add( mComboMasterGain ); - - /* Mixer Gain Control */ - mComboMixerGain = new JComboBox( R820TMixerGain.values() ); - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - try - { - R820TMixerGain mixerGain = - (R820TMixerGain)mComboMixerGain.getSelectedItem(); - - if( mixerGain == null ) - { - mixerGain = DEFAULT_GAIN.getMixerGain(); - } - - if( mComboMixerGain.isEnabled() ) - { - mController.setMixerGain( mixerGain, true ); - } - - mSelectedConfig.setMixerGain( mixerGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't apply the mixer " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply mixer " - + "gain setting - ", e ); - } - } - } ); - } - } ); - mComboMixerGain.setToolTipText( "Mixer Gain. Set master gain " - + "to MASTER to enable adjustment" ); - mComboMixerGain.setEnabled( false ); - gainPanel.add( new JLabel( "Mixer" ) ); - gainPanel.add( mComboMixerGain, "wrap" ); - - /* LNA Gain Control */ - mComboLNAGain = new JComboBox( R820TLNAGain.values() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TLNAGain lnaGain = - (R820TLNAGain)mComboLNAGain.getSelectedItem(); - - if ( lnaGain == null ) - { - lnaGain = DEFAULT_GAIN.getLNAGain(); - } - - if( mComboLNAGain.isEnabled() ) - { - mController.setLNAGain( lnaGain, true ); - } - - mSelectedConfig.setLNAGain( lnaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't apply the LNA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply LNA " - + "gain setting - ", e ); - } - } - } ); - mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboLNAGain.setEnabled( false ); - gainPanel.add( new JLabel( "LNA" ) ); - gainPanel.add( mComboLNAGain ); - - /* VGA Gain Control */ - mComboVGAGain = new JComboBox( R820TVGAGain.values() ); - mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); - mComboVGAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - try - { - R820TVGAGain vgaGain = - (R820TVGAGain)mComboVGAGain.getSelectedItem(); - - if( vgaGain == null ) - { - vgaGain = DEFAULT_GAIN.getVGAGain(); - } - - if( mComboVGAGain.isEnabled() ) - { - mController.setVGAGain( vgaGain, true ); - } - - mSelectedConfig.setVGAGain( vgaGain ); - save(); - } - catch ( UsbException e ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't apply the VGA " - + "gain setting - " + e.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply VGA " - + "gain setting", e ); - } - } - } ); - mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " - + "to MANUAL to enable adjustment" ); - mComboVGAGain.setEnabled( false ); - gainPanel.add( new JLabel( "VGA" ) ); - gainPanel.add( mComboVGAGain, "wrap" ); - - add( gainPanel, "span" ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { -// TunerConfiguration config = -// mSettingsManager -// .addNewTunerConfiguration( -// TunerType.RAFAELMICRO_R820T, -// "New Configuration" ); -// -// mComboConfigurations.setModel( getModel() ); -// -// mComboConfigurations.setSelectedItem( config ); -// -// repaint(); - } - } ); - - add( mNewConfiguration, "growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - R820TTunerConfiguration selected = - (R820TTunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - R820TTunerConfigurationEditorOld.this, - "Are you sure you want to delete '" - + selected.getName() + "'?", - "Are you sure?", - JOptionPane.YES_NO_OPTION ); - - if( n == JOptionPane.YES_OPTION ) - { -// mSettingsManager -// .deleteTunerConfiguration( selected ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedIndex( 0 ); - - repaint(); - } - } - } - } ); - - add( mDeleteConfiguration, "growx,push,wrap" ); - } - - /** - * Updates gui controls with the values from the tuner configuration - * @param config - tuner configuration - */ - private void update( R820TTunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); - - /* Apply master gain last so that the Mixer, LNA, and VGA gain - * settings are updated with the master setting where necessary */ - mComboMasterGain.setSelectedItem( mSelectedConfig.getMasterGain() ); - - mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); - -// mSettingsManager.setSelectedTunerConfiguration( -// TunerType.RAFAELMICRO_R820T, mController.getUniqueID(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - R820TTunerConfigurationEditorOld.this, - "R820T Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "R820T Tuner Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - /** - * Constructs a combo box model for the tuner configuration combo component - * by assembling a list of the tuner configurations appropriate for this - * specific tuner - */ - private ComboBoxModel getModel() - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - -// for( TunerConfiguration config: configs ) -// { -// model.addElement( (R820TTunerConfiguration)config ); -// } - - return model; - } - - /** - * Retrieves a specific (named) tuner configuration - */ - private R820TTunerConfiguration getNamedConfiguration( String name ) - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.RAFAELMICRO_R820T ); -// -// for( TunerConfiguration config: configs ) -// { -// if( config.getName().contentEquals( name ) ) -// { -// return (R820TTunerConfiguration)config; -// } -// } - - return null; - } - - /** - * Saves current tuner configuration settings - */ - private void save() - { -// mSettingsManager.save(); - } -} diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index 6fddb93e..7c325eb0 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -59,8 +59,7 @@ import source.tuner.rtl.r820t.R820TTunerController.R820TVGAGain; public class R820TTunerEditor extends TunerConfigurationEditor { - private final static Logger mLog = - LoggerFactory.getLogger( R820TTunerEditor.class ); + private final static Logger mLog = LoggerFactory.getLogger( R820TTunerEditor.class ); private static final long serialVersionUID = 1L; private static final R820TGain DEFAULT_GAIN = R820TGain.GAIN_279; @@ -69,16 +68,15 @@ public class R820TTunerEditor extends TunerConfigurationEditor private JTextField mConfigurationName; private JButton mTunerInfo; + private JComboBox mComboSampleRate; private JSpinner mFrequencyCorrection; private JComboBox mComboMasterGain; private JComboBox mComboMixerGain; private JComboBox mComboLNAGain; private JComboBox mComboVGAGain; - private JComboBox mComboSampleRate; private boolean mLoading; - public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, - RTL2832Tuner tuner ) + public R820TTunerEditor( TunerConfigurationModel tunerConfigurationModel, RTL2832Tuner tuner ) { super( tunerConfigurationModel ); mController = (R820TTunerController)tuner.getController(); @@ -103,9 +101,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor add( new JLabel( "R820T Tuner Configuration" ), "span,align center" ); - /** - * Tuner Configuration Name - */ mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); mConfigurationName.addFocusListener( new FocusListener() @@ -215,7 +210,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor /** * Gain Controls */ - add( new JLabel( "Gain" ), "span,align left" ); + add( new JLabel( "Gain" ), "warp" ); /* Master Gain Control */ mComboMasterGain = new JComboBox( R820TGain.values() ); From 4adffbdca7e5fc6aa8d0ca442d17e5f61404ce1e Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Mar 2016 06:40:25 -0400 Subject: [PATCH 65/74] wip #34 adds sort ordering to tuner table --- src/source/tuner/TunerEditor.java | 6 +++++- src/source/tuner/TunerViewPanel.java | 13 +++++++++++++ .../configuration/TunerConfigurationModel.java | 6 +++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index e1a2beb6..9489f84f 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -7,6 +7,8 @@ import gui.editor.EmptyEditor; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.List; import javax.swing.JButton; import javax.swing.JLabel; @@ -15,7 +17,9 @@ import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.RowFilter; +import javax.swing.RowSorter; import javax.swing.ScrollPaneConstants; +import javax.swing.SortOrder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.TableRowSorter; @@ -67,8 +71,8 @@ public class TunerEditor extends Editor mFrequencyControl.setEnabled( false ); listPanel.add( mFrequencyControl, "span" ); - mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); mRowSorter = new TableRowSorter<>( mTunerConfigurationModel ); + mTunerConfigurationTable = new JTable( mTunerConfigurationModel ); mTunerConfigurationTable.setRowSorter( mRowSorter ); mTunerConfigurationTable.setEnabled( false ); mTunerConfigurationTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() diff --git a/src/source/tuner/TunerViewPanel.java b/src/source/tuner/TunerViewPanel.java index 3bedee68..b6ab262a 100644 --- a/src/source/tuner/TunerViewPanel.java +++ b/src/source/tuner/TunerViewPanel.java @@ -5,16 +5,21 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.ListSelectionModel; +import javax.swing.RowSorter; +import javax.swing.SortOrder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableRowSorter; import net.miginfocom.swing.MigLayout; @@ -32,6 +37,7 @@ public class TunerViewPanel extends JPanel private TunerModel mTunerModel; private JTable mTunerTable; + private TableRowSorter mRowSorter; private JideSplitPane mSplitPane; private TunerEditor mTunerEditor; @@ -47,7 +53,14 @@ public class TunerViewPanel extends JPanel { setLayout( new MigLayout( "insets 0 0 0 0", "[fill,grow]", "[fill,grow]" ) ); + mRowSorter = new TableRowSorter<>( mTunerModel ); + List sortKeys = new ArrayList<>(); + sortKeys.add( new RowSorter.SortKey( TunerModel.TUNER_TYPE, SortOrder.ASCENDING ) ); + sortKeys.add( new RowSorter.SortKey( TunerModel.TUNER_ID, SortOrder.ASCENDING ) ); + mRowSorter.setSortKeys( sortKeys ); + mTunerTable = new JTable( mTunerModel ); + mTunerTable.setRowSorter( mRowSorter ); mTunerTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); mTunerTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() { diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index daa37793..9b0e1c1d 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -18,9 +18,9 @@ public class TunerConfigurationModel extends AbstractTableModel private final static Logger mLog = LoggerFactory.getLogger( TunerConfigurationModel.class ); - private static final int TUNER_TYPE = 0; - private static final int CONFIGURATION_NAME = 1; - private static final int ASSIGNED = 2; + public static final int TUNER_TYPE = 0; + public static final int CONFIGURATION_NAME = 1; + public static final int ASSIGNED = 2; private static final String[] COLUMN_HEADERS = { "Tuner Type", "Name", "Assigned" }; private List> mConfigurationChangeListeners = From 13617e8fe28545ac34bd2d2c8816c2fa0a2e550d Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Mar 2016 07:22:18 -0400 Subject: [PATCH 66/74] wip #34 updates funcube dongle pro plus tuner editor --- src/source/mixer/MixerManager.java | 10 +- src/source/tuner/TunerEditor.java | 18 + src/source/tuner/TunerManager.java | 1 + src/source/tuner/TunerViewPanel.java | 18 + .../TunerConfigurationFactory.java | 22 +- .../TunerConfigurationModel.java | 18 + src/source/tuner/fcd/FCDTunerController.java | 15 +- .../tuner/fcd/proV1/FCD1TunerController.java | 11 - .../fcd/proplusV2/FCD2TunerController.java | 111 ++---- .../tuner/fcd/proplusV2/FCD2TunerEditor.java | 327 ++++++++++++++++++ .../fcd/proplusV2/FCD2TunerEditorPanel.java | 110 ------ 11 files changed, 450 insertions(+), 211 deletions(-) create mode 100644 src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java delete mode 100644 src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java diff --git a/src/source/mixer/MixerManager.java b/src/source/mixer/MixerManager.java index d7af001a..2026e6c6 100644 --- a/src/source/mixer/MixerManager.java +++ b/src/source/mixer/MixerManager.java @@ -19,6 +19,7 @@ package source.mixer; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.List; @@ -168,7 +169,14 @@ public class MixerManager public Collection getMixerTunerDataLines() { - return mMixerTuners.values(); + if( mMixerTuners.isEmpty() ) + { + return Collections.emptyList(); + } + else + { + return mMixerTuners.values(); + } } private void loadMixers() diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 9489f84f..667a51d3 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ + package source.tuner; import gui.control.JFrequencyControl; diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index d423d860..926044b3 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -54,6 +54,7 @@ public class TunerManager TunerModel tunerModel, ThreadPoolManager threadPoolManager ) { + mMixerManager = mixerManager; mTunerModel = tunerModel; mThreadPoolManager = threadPoolManager; diff --git a/src/source/tuner/TunerViewPanel.java b/src/source/tuner/TunerViewPanel.java index b6ab262a..306f2adb 100644 --- a/src/source/tuner/TunerViewPanel.java +++ b/src/source/tuner/TunerViewPanel.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ + package source.tuner; import java.awt.Color; diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java index 1851bec7..2ac7501e 100644 --- a/src/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ + package source.tuner.configuration; import gui.editor.Editor; @@ -7,8 +25,10 @@ import source.tuner.TunerType; import source.tuner.airspy.AirspyTuner; import source.tuner.airspy.AirspyTunerConfiguration; import source.tuner.airspy.AirspyTunerEditor; +import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; +import source.tuner.fcd.proplusV2.FCD2TunerEditor; import source.tuner.hackrf.HackRFTuner; import source.tuner.hackrf.HackRFTunerConfiguration; import source.tuner.hackrf.HackRFTunerEditor; @@ -62,7 +82,7 @@ public class TunerConfigurationFactory case FUNCUBE_DONGLE_PRO: return new EmptyEditor( "a tuner" ); case FUNCUBE_DONGLE_PRO_PLUS: - return new EmptyEditor( "a tuner" ); + return new FCD2TunerEditor( model, (FCDTuner)tuner ); case HACKRF: return new HackRFTunerEditor( model, (HackRFTuner)tuner ); case RAFAELMICRO_R820T: diff --git a/src/source/tuner/configuration/TunerConfigurationModel.java b/src/source/tuner/configuration/TunerConfigurationModel.java index 9b0e1c1d..717e1790 100644 --- a/src/source/tuner/configuration/TunerConfigurationModel.java +++ b/src/source/tuner/configuration/TunerConfigurationModel.java @@ -1,3 +1,21 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ + package source.tuner.configuration; import java.util.ArrayList; diff --git a/src/source/tuner/fcd/FCDTunerController.java b/src/source/tuner/fcd/FCDTunerController.java index b0d8416d..7deb268c 100644 --- a/src/source/tuner/fcd/FCDTunerController.java +++ b/src/source/tuner/fcd/FCDTunerController.java @@ -147,23 +147,10 @@ public abstract class FCDTunerController extends TunerController */ public abstract TunerType getTunerType(); - /** - * Editor panel (GUI) component. - */ - public abstract JPanel getEditor( FCDTuner tuner, - SettingsManager settingsManager ); - /** * Applies the settings in the tuner configuration */ - public abstract void apply( TunerConfiguration config ) - throws SourceException; - - /** - * Returns the currently applied tuner configuration or null if one hasn't - * yet been applied - */ - public abstract TunerConfiguration getTunerConfiguration(); + public abstract void apply( TunerConfiguration config ) throws SourceException; /** * USB address (bus/port) diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index 27354fc0..adac5c37 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -149,17 +149,6 @@ public class FCD1TunerController extends FCDTunerController } } - @Override - public JPanel getEditor( FCDTuner tuner, SettingsManager settingsManager ) - { - if( mEditor == null ) - { - mEditor = new FCD1TunerEditorPanel( tuner, settingsManager ); - } - - return mEditor; - } - /** * Gets the current LNA Gain value from the controller and stores it */ diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java index df579ae4..7a27c286 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerController.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerController.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,49 +20,39 @@ package source.tuner.fcd.proplusV2; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import javax.swing.JPanel; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.Device; import org.usb4java.DeviceDescriptor; -import settings.SettingsManager; import source.SourceException; import source.tuner.MixerTunerType; import source.tuner.TunerClass; import source.tuner.TunerType; import source.tuner.configuration.TunerConfiguration; import source.tuner.fcd.FCDCommand; -import source.tuner.fcd.FCDTuner; import source.tuner.fcd.FCDTunerController; public class FCD2TunerController extends FCDTunerController { - private final static Logger mLog = - LoggerFactory.getLogger( FCD2TunerController.class ); + private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerController.class ); - public static final int sMINIMUM_TUNABLE_FREQUENCY = 150000; - public static final int sMAXIMUM_TUNABLE_FREQUENCY = 2050000000; - public static final int sSAMPLE_RATE = 192000; - - private FCD2TunerConfiguration mTunerConfiguration; - private FCD2TunerEditorPanel mEditor; + public static final int MINIMUM_TUNABLE_FREQUENCY = 150000; + public static final int MAXIMUM_TUNABLE_FREQUENCY = 2050000000; + public static final int SAMPLE_RATE = 192000; public FCD2TunerController( Device device, DeviceDescriptor descriptor ) { - super( device, - descriptor, + super( device, descriptor, (int)MixerTunerType.FUNCUBE_DONGLE_PRO_PLUS.getAudioFormat().getSampleRate(), - sMINIMUM_TUNABLE_FREQUENCY, - sMAXIMUM_TUNABLE_FREQUENCY ); + MINIMUM_TUNABLE_FREQUENCY, MAXIMUM_TUNABLE_FREQUENCY ); } public void init() throws SourceException { super.init(); - mFrequencyController.setSampleRate( sSAMPLE_RATE ); + mFrequencyController.setSampleRate( SAMPLE_RATE ); try { @@ -76,7 +66,7 @@ public class FCD2TunerController extends FCDTunerController public int getCurrentSampleRate() { - return sSAMPLE_RATE; + return SAMPLE_RATE; } @Override @@ -90,71 +80,44 @@ public class FCD2TunerController extends FCDTunerController { return TunerType.FUNCUBE_DONGLE_PRO_PLUS; } - - public JPanel getEditor( FCDTuner tuner, SettingsManager settingsManager ) - { - if( mEditor == null ) - { - mEditor = new FCD2TunerEditorPanel( tuner, settingsManager ); - } - - return mEditor; - } + public void setLNAGain( boolean enabled ) throws SourceException + { + try + { + send( FCDCommand.APP_SET_LNA_GAIN, enabled ? 1 : 0 ); + } + catch ( Exception e ) + { + throw new SourceException( "error while setting LNA Gain", e ); + } + } + + public void setMixerGain( boolean enabled ) throws SourceException + { + try + { + send( FCDCommand.APP_SET_MIXER_GAIN, enabled ? 1 : 0 ); + } + catch ( Exception e ) + { + throw new SourceException( "error while setting Mixer Gain", e ); + } + } + @Override public void apply( TunerConfiguration config ) throws SourceException { if( config instanceof FCD2TunerConfiguration ) { - FCD2TunerConfiguration plusConfig = - (FCD2TunerConfiguration)config; + FCD2TunerConfiguration fcd2 = (FCD2TunerConfiguration)config; - setFrequencyCorrection( plusConfig.getFrequencyCorrection() ); - - try - { - if( plusConfig.getGainLNA() ) - { - send( FCDCommand.APP_SET_LNA_GAIN, 1 ); - - } - else - { - send( FCDCommand.APP_SET_LNA_GAIN, 0 ); - } - } - catch ( Exception e ) - { - throw new SourceException( "error while setting LNA Gain", e ); - } - - try - { - if( plusConfig.getGainMixer() ) - { - send( FCDCommand.APP_SET_MIXER_GAIN, 1 ); - - } - else - { - send( FCDCommand.APP_SET_MIXER_GAIN, 0 ); - } - } - catch ( Exception e ) - { - throw new SourceException( "error while setting Mixer Gain", e ); - } - - mTunerConfiguration = plusConfig; + setFrequencyCorrection( fcd2.getFrequencyCorrection() ); + setLNAGain( fcd2.getGainLNA() ); + setMixerGain( fcd2.getGainMixer() ); } } - @Override - public TunerConfiguration getTunerConfiguration() - { - return mTunerConfiguration; - } - public int getDCCorrection() { int dcCorrection = -999; diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java new file mode 100644 index 00000000..d358c46f --- /dev/null +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source.tuner.fcd.proplusV2; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceException; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationEvent.Event; +import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.fcd.FCDTuner; + +public class FCD2TunerEditor extends TunerConfigurationEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( FCD2TunerEditor.class ); + private static final long serialVersionUID = 1L; + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JSpinner mFrequencyCorrection; + private JCheckBox mLNAGain; + private JCheckBox mMixerGain; + private boolean mLoading; + + private FCD2TunerController mController; + + public FCD2TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) + { + super( tunerConfigurationModel ); + mController = (FCD2TunerController)tuner.getController(); + + init(); + } + + private FCD2TunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (FCD2TunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][grow]" ) ); + + add( new JLabel( "FCD Pro+ Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( FCD2TunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( FCD2TunerEditor.this, "FCD Pro Plus Tuner " + + "Controller - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + + mLNAGain = new JCheckBox( "LNA Gain" ); + mLNAGain.setEnabled( false ); + mLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + mController.setLNAGain( mLNAGain.isSelected() ); + save(); + } + catch( SourceException e ) + { + mLog.error( "Couldn't set LNA gain for FCD2", e ); + } + } + } ); + + add( new JLabel( "" ) ); + add( mLNAGain ); + + /** + * Mixer Gain + */ + mMixerGain = new JCheckBox( "Mixer Gain" ); + + mMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent event ) + { + try + { + mController.setMixerGain( mMixerGain.isSelected() ); + save(); + } + catch( SourceException e ) + { + mLog.error( "Couldn't set mixer gain for FCD2", e ); + } + } + } ); + + add( new JLabel( "" ) ); + add( mMixerGain ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mLNAGain.isEnabled() != enabled ) + { + mLNAGain.setEnabled( enabled ); + } + + if( mMixerGain.isEnabled() != enabled ) + { + mMixerGain.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "

    Funcube Dongle Pro Plus Tuner

    " ); + + sb.append( "USB ID: " ); + sb.append( mController.getUSBID() ); + sb.append( "
    " ); + + sb.append( "USB Address: " ); + sb.append( mController.getUSBAddress() ); + sb.append( "
    " ); + + sb.append( "USB Speed: " ); + sb.append( mController.getUSBSpeed() ); + sb.append( "
    " ); + + sb.append( "Cellular Band: " ); + sb.append( mController.getConfiguration().getBandBlocking() ); + sb.append( "
    " ); + + sb.append( "Firmware: " ); + sb.append( mController.getConfiguration().getFirmware() ); + sb.append( "
    " ); + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + FCD2TunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + + config.setGainLNA( mLNAGain.isSelected() ); + config.setGainMixer( mMixerGain.isSelected() ); + + getTunerConfigurationModel().broadcast( + new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + if( hasItem() ) + { + FCD2TunerConfiguration config = getConfiguration(); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mLNAGain.setSelected( config.getGainLNA() ); + mMixerGain.setSelected( config.getGainMixer() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + + mLoading = false; + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + } +} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java deleted file mode 100644 index 8bda26c0..00000000 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditorPanel.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.fcd.proplusV2; - -import gui.control.JFrequencyControl; - -import java.awt.Color; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.fcd.FCDTuner; -import source.tuner.fcd.FCDTunerDetailsPanel; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.IFrequencyChangeProcessor; - -import com.jidesoft.swing.JideTabbedPane; - -public class FCD2TunerEditorPanel extends JPanel implements IFrequencyChangeProcessor -{ - private final static Logger mLog = - LoggerFactory.getLogger( FCD2TunerEditorPanel.class ); - private static final long serialVersionUID = 1L; - - private FCDTuner mTuner; - private FCD2TunerController mController; - private SettingsManager mSettingsManager; - - private JFrequencyControl mFrequencyControl; - - public FCD2TunerEditorPanel( FCDTuner tuner, SettingsManager settingsManager ) - { - mTuner = tuner; - mController = (FCD2TunerController)mTuner.getController(); - mSettingsManager = settingsManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - - add( new JLabel( "Funcube Dongle Pro Plus Tuner" ), "span,align center" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as frequency change listener. This creates a - * feedback loop, so the control does not rebroadcast the event */ - mTuner.getController().addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span,align center" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.add( "Configuration", new FCD2TunerConfigurationPanel( - mSettingsManager, mController ) ); - - tabs.add( "Info", new FCDTunerDetailsPanel( mController ) ); - - add( tabs, "span,grow,push" ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - //Disregard the bandwidth, which shouldn't be changing - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "FCDProController - error setting frequency [" + - event.getValue().longValue() + "]", e ); - } - } - } -} From f32c520caba437962fe27245b26eec21e32a38ba Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Mar 2016 08:13:28 -0400 Subject: [PATCH 67/74] wip #34 - updates FCD Pro tuner and completes work on tuners, tuner configurations and tuner editors. --- src/source/tuner/TunerEditor.java | 1 + .../TunerConfigurationFactory.java | 11 +- .../tuner/fcd/FCDTunerDetailsPanel.java | 49 -- .../fcd/proV1/FCD1TunerConfiguration.java | 3 +- .../proV1/FCD1TunerConfigurationPanel.java | 657 ------------------ .../tuner/fcd/proV1/FCD1TunerController.java | 2 +- .../tuner/fcd/proV1/FCD1TunerEditor.java | 528 ++++++++++++++ .../tuner/fcd/proV1/FCD1TunerEditorPanel.java | 110 --- .../FCD2TunerConfigurationPanel.java | 376 ---------- 9 files changed, 537 insertions(+), 1200 deletions(-) delete mode 100644 src/source/tuner/fcd/FCDTunerDetailsPanel.java delete mode 100644 src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java create mode 100644 src/source/tuner/fcd/proV1/FCD1TunerEditor.java delete mode 100644 src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java delete mode 100644 src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java diff --git a/src/source/tuner/TunerEditor.java b/src/source/tuner/TunerEditor.java index 667a51d3..2c7b7f1b 100644 --- a/src/source/tuner/TunerEditor.java +++ b/src/source/tuner/TunerEditor.java @@ -158,6 +158,7 @@ public class TunerEditor extends Editor TunerConfiguration selected = getSelectedTunerConfiguration(); mTunerConfigurationModel.removeTunerConfiguration( selected ); + mTunerConfigurationTable.setRowSelectionInterval( 0, 0 ); } } } ); diff --git a/src/source/tuner/configuration/TunerConfigurationFactory.java b/src/source/tuner/configuration/TunerConfigurationFactory.java index 2ac7501e..181440b4 100644 --- a/src/source/tuner/configuration/TunerConfigurationFactory.java +++ b/src/source/tuner/configuration/TunerConfigurationFactory.java @@ -19,7 +19,6 @@ package source.tuner.configuration; import gui.editor.Editor; -import gui.editor.EmptyEditor; import source.tuner.Tuner; import source.tuner.TunerType; import source.tuner.airspy.AirspyTuner; @@ -27,6 +26,7 @@ import source.tuner.airspy.AirspyTunerConfiguration; import source.tuner.airspy.AirspyTunerEditor; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerConfiguration; +import source.tuner.fcd.proV1.FCD1TunerEditor; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerEditor; import source.tuner.hackrf.HackRFTuner; @@ -43,8 +43,8 @@ public class TunerConfigurationFactory /** * Creates a tuner configuration for the specified tuner type, unique ID and name */ - public static TunerConfiguration getTunerConfiguration( TunerType type, - String uniqueID, String name ) + public static TunerConfiguration getTunerConfiguration( TunerType type, String uniqueID, + String name ) { switch( type ) { @@ -70,8 +70,7 @@ public class TunerConfigurationFactory /** * Creates a tuner editor gui for the specified tuner */ - public static Editor getEditor( Tuner tuner, - TunerConfigurationModel model ) + public static Editor getEditor( Tuner tuner, TunerConfigurationModel model ) { switch( tuner.getTunerType() ) { @@ -80,7 +79,7 @@ public class TunerConfigurationFactory case ELONICS_E4000: return new E4KTunerEditor( model, (RTL2832Tuner)tuner ); case FUNCUBE_DONGLE_PRO: - return new EmptyEditor( "a tuner" ); + return new FCD1TunerEditor( model, (FCDTuner)tuner ); case FUNCUBE_DONGLE_PRO_PLUS: return new FCD2TunerEditor( model, (FCDTuner)tuner ); case HACKRF: diff --git a/src/source/tuner/fcd/FCDTunerDetailsPanel.java b/src/source/tuner/fcd/FCDTunerDetailsPanel.java deleted file mode 100644 index 05e3b51f..00000000 --- a/src/source/tuner/fcd/FCDTunerDetailsPanel.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.fcd; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -public class FCDTunerDetailsPanel extends JPanel -{ - private static final long serialVersionUID = 1L; - - public FCDTunerDetailsPanel( FCDTunerController controller ) - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - add( new JLabel( "USB ID:" ) ); - add( new JLabel( controller.getUSBID() ) ); - - add( new JLabel( "USB Address:" ) ); - add( new JLabel( controller.getUSBAddress() ) ); - - add( new JLabel( "USB Speed:" ) ); - add( new JLabel( controller.getUSBSpeed() ) ); - - add( new JLabel( "Cellular Band:" ) ); - add( new JLabel( controller.getConfiguration().getBandBlocking() - .getLabel() ) ); - - add( new JLabel( "Firmware:" ) ); - add( new JLabel( controller.getConfiguration().getFirmware() ) ); - } -} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java index 9c474376..b045893f 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerConfiguration.java @@ -55,6 +55,7 @@ public class FCD1TunerConfiguration extends TunerConfiguration return TunerType.FUNCUBE_DONGLE_PRO; } + @XmlAttribute( name = "lna_gain" ) public LNAGain getLNAGain() { return mLNAGain; @@ -65,12 +66,12 @@ public class FCD1TunerConfiguration extends TunerConfiguration mLNAGain = gain; } - @XmlAttribute( name = "lna_gain" ) public LNAEnhance getLNAEnhance() { return mLNAEnhance; } + @XmlAttribute( name = "lna_enhance" ) public void setLNAEnhance( LNAEnhance enhance ) { mLNAEnhance = enhance; diff --git a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java deleted file mode 100644 index 390f3f21..00000000 --- a/src/source/tuner/fcd/proV1/FCD1TunerConfigurationPanel.java +++ /dev/null @@ -1,657 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.fcd.proV1; - -import java.awt.Color; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; -import java.util.ArrayList; - -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.usb.UsbClaimException; -import javax.usb.UsbException; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.TunerType; -import source.tuner.configuration.TunerConfiguration; -import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; -import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; -import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; - -import com.jidesoft.swing.JideTabbedPane; - -public class FCD1TunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( FCD1TunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - private SettingsManager mSettingsManager; - private FCD1TunerController mController; - - private FCD1TunerConfiguration mSelectedConfig; - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - private JComboBox mComboLNAGain; - private JComboBox mComboLNAEnhance; - private JComboBox mComboMixerGain; - private JTextField mName; - private JSpinner mCorrectionFrequency; - private CorrectionSpinner mCorrectionDCI; - private CorrectionSpinner mCorrectionDCQ; - private CorrectionSpinner mCorrectionGain; - private CorrectionSpinner mCorrectionPhase; - - public FCD1TunerConfigurationPanel( SettingsManager settingsManager, - FCD1TunerController controller ) - { - mSettingsManager = settingsManager; - mController = controller; - mSelectedConfig = controller.getTunerConfiguration(); - - init(); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][grow]" ) ); - - /** - * Tuner configurations combo - */ - mComboConfigurations = new JComboBox(); - mComboConfigurations.setToolTipText( "Select a tuner configuration. " - + "Create a separate tuner configuration for each of your " - + "funcube dongles by using the NEW button and typing a " - + "descriptive name" ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD1TunerConfiguration selected = - (FCD1TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - /** - * Tuner Configuration - Name - */ - mName = new JTextField( mSelectedConfig.getName() ); - mName.setToolTipText( "Name for the selected tuner configuration" ); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - mSelectedConfig.setName( mName.getText() ); - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name:" ) ); - add( mName, "growx,push" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - add( tabs, "span,grow,push" ); - - JPanel gainPanel = new JPanel(); - gainPanel.setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][grow]" ) ); - - /** - * LNA Gain - */ - mComboLNAGain = new JComboBox( LNAGain.values() ); - - mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain " - + "setting. Default value is 20db" ); - - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - - mComboLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); - - mSelectedConfig.setLNAGain( gain ); - - try - { - mController.setLNAGain( gain ); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply LNA gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying LNA gain value [" + - gain.toString() + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - FCD Controller " - + "cannot apply LNA gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - " - + "exception while applying LNA gain value [" + - gain.toString() + "]", e1 ); - } - - save(); - } - - } ); - - gainPanel.add( new JLabel( "LNA:" ) ); - gainPanel.add( mComboLNAGain, "growx,push" ); - - /** - * LNA Enhance - */ - mComboLNAEnhance = new JComboBox( LNAEnhance.values() ); - mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. " - + "Default value is OFF" ); - - mComboLNAEnhance.setSelectedItem( mSelectedConfig.getLNAEnhance() ); - - mComboLNAEnhance.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); - - mSelectedConfig.setLNAEnhance( enhance ); - - try - { - mController.setLNAEnhance( enhance ); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply LNA enhance [" + enhance.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying LNA enhance value [" + - enhance.toString() + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - FCD Controller " - + "cannot apply LNA enhance [" + enhance.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - " - + "exception while applying LNA enhance value [" + - enhance.toString() + "]", e1 ); - } - - save(); - } - - } ); - - gainPanel.add( new JLabel( "LNA Enhance:" ) ); - gainPanel.add( mComboLNAEnhance, "growx,push" ); - - /** - * Mixer Gain - */ - mComboMixerGain = new JComboBox( MixerGain.values() ); - mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); - - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - - mComboMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); - - mSelectedConfig.setMixerGain( gain ); - - try - { - mController.setMixerGain( gain ); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply Mixer gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying Mixer gain value [" + - gain.toString() + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - FCD Controller " - + "cannot apply Mixer gain [" + gain.toString() + "]" ); - - mLog.error( "FuncubeDonglePro Controller - " - + "exception while applying Mixer gain value [" + - gain.toString() + "]", e1 ); - } - - save(); - } - - } ); - - gainPanel.add( new JLabel( "Mixer:" ) ); - gainPanel.add( mComboMixerGain, "growx,push" ); - - tabs.add( "Gain", gainPanel ); - - JPanel correctionPanel = new JPanel(); - correctionPanel.setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - /** - * Frequency Correction - */ - SpinnerModel model = - new SpinnerNumberModel( 0.0, -1000.0, 1000.0, 0.1 ); //initial,min,max,step - - mCorrectionFrequency = new JSpinner( model ); - - mCorrectionFrequency.setToolTipText( "Frequency Correction: adjust " - + "value +/- to align the displayed frequency label with the " - + "actual signals" ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mCorrectionFrequency.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mCorrectionFrequency.setValue( mSelectedConfig.getFrequencyCorrection() ); - - mCorrectionFrequency.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = ((SpinnerNumberModel)mCorrectionFrequency - .getModel()).getNumber().doubleValue(); - - try - { - mSelectedConfig.setFrequencyCorrection( value ); - mController.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - couldn't " - + "apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDonglePro Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - correctionPanel.add( new JLabel( "Frequency PPM:" ) ); - correctionPanel.add( mCorrectionFrequency, "growx,push" ); - - /** - * Inphase DC Correction - */ - mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, - mSelectedConfig.getInphaseDCCorrection(), 0.00001, 5 ); - mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "DC Inphase:" ) ); - correctionPanel.add( mCorrectionDCI, "growx,push" ); - - /** - * Quadrature DC Correction - */ - mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, - mSelectedConfig.getQuadratureDCCorrection(), 0.00001, 5 ); - mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " - + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "DC Quadrature:" ) ); - correctionPanel.add( mCorrectionDCQ, "growx,push" ); - - /** - * Gain Correction - */ - mCorrectionGain = new CorrectionSpinner( Correction.GAIN, - mSelectedConfig.getGainCorrection(), 0.00001, 5 ); - mCorrectionGain.setToolTipText( "Gain Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "Gain:" ) ); - correctionPanel.add( mCorrectionGain, "growx,push" ); - - /** - * Phase Correction - */ - mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, - mSelectedConfig.getPhaseCorrection(), 0.00001, 5 ); - mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " - + "-1.0 to 1.0 (default: 0.0)" ); - correctionPanel.add( new JLabel( "Phase:" ) ); - correctionPanel.add( mCorrectionPhase, "growx,push" ); - - tabs.add( "Correction", correctionPanel ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - mNewConfiguration.setToolTipText( "Create a new tuner configuration " - + "for each of your dongles to keep track of the specific " - + "settings for each dongle" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { -// TunerConfiguration config = -// mSettingsManager.addNewTunerConfiguration( -// TunerType.FUNCUBE_DONGLE_PRO, -// "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - -// mComboConfigurations.setSelectedItem( config ); - - repaint(); - } - } ); - - add( mNewConfiguration, "growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - mDeleteConfiguration.setToolTipText( "Deletes the currently selected " - + "tuner configuration. Note: the DEFAULT tuner " - + "configuration will be recreated if you delete it, " - + "after you restart the application" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD1TunerConfiguration selected = - (FCD1TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - FCD1TunerConfigurationPanel.this, - "Are you sure you want to delete '" - + selected.getName() + "'?", - "Are you sure?", - JOptionPane.YES_NO_OPTION ); - - if( n == JOptionPane.YES_OPTION ) - { -// mSettingsManager.deleteTunerConfiguration( selected ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedIndex( 0 ); - - repaint(); - } - } - } - } ); - - add( mDeleteConfiguration, "growx,push" ); - } - - private void update( FCD1TunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - - mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); - mComboLNAEnhance.setSelectedItem( mSelectedConfig.getLNAEnhance() ); - mComboMixerGain.setSelectedItem( mSelectedConfig.getMixerGain() ); - - mCorrectionFrequency.setValue( config.getFrequencyCorrection() ); - mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); - mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); - mCorrectionGain.setValue( config.getGainCorrection() ); - mCorrectionPhase.setValue( config.getPhaseCorrection() ); - -// mSettingsManager.setSelectedTunerConfiguration( -// TunerType.FUNCUBE_DONGLE_PRO, -// mController.getUSBAddress(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDonglePro Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - private ComboBoxModel getModel() - { -// ArrayList configs = -// mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - -// for( TunerConfiguration config: configs ) -// { -// model.addElement( (FCD1TunerConfiguration)config ); -// } - - return model; - } - - @SuppressWarnings( "unused" ) - private FCD1TunerConfiguration getNamedConfiguration( String name ) - { -// ArrayList configs = -// mSettingsManager.getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO ); - -// for( TunerConfiguration config: configs ) -// { -// if( config.getName().contentEquals( name ) ) -// { -// return (FCD1TunerConfiguration)config; -// } -// } - - return null; - } - - private void save() - { -// mSettingsManager.save(); - } - - public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; - - public class CorrectionSpinner extends JSpinner - { - private static final long serialVersionUID = 1L; - private static final double sMIN_VALUE = -1.0d; - private static final double sMAX_VALUE = 1.0d; - - private Correction mCorrectionComponent; - - public CorrectionSpinner( Correction component, - double initialValue, - double step, - int decimalPlaces ) - { - mCorrectionComponent = component; - - SpinnerModel model = new SpinnerNumberModel( initialValue, - sMIN_VALUE, sMAX_VALUE, step ); - - setModel( model ); - - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( decimalPlaces ); - - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = - ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); - - try - { - switch( mCorrectionComponent ) - { - case DC_INPHASE: - mSelectedConfig.setInphaseDCCorrection( value ); - mController.setDCCorrectionInPhase( value ); - break; - case DC_QUADRATURE: - mSelectedConfig.setQuadratureDCCorrection( value ); - mController.setDCCorrectionQuadrature( value ); - break; - case GAIN: - mSelectedConfig.setGainCorrection( value ); - mController.setGainCorrection( value ); - break; - case PHASE: - mSelectedConfig.setPhaseCorrection( value ); - mController.setPhaseCorrection( value ); - break; - } - - //Save the change(s) to the selected tuner config - save(); - } - catch ( UsbClaimException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - cannot claim FCD " - + "Controller to apply " - + mCorrectionComponent.toString() + - " correction value [" + value + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb claim " - + "exception while applying " - + mCorrectionComponent.toString() - + " correction value [" + value + "]", e1 ); - } - catch ( UsbException e1 ) - { - JOptionPane.showMessageDialog( - FCD1TunerConfigurationPanel.this, - "FCD Pro Tuner Controller - USB error from FCD " - + "Controller while applying " - + mCorrectionComponent.toString() + - " correction value [" + value + "]" ); - - mLog.error( "FuncubeDonglePro Controller - usb " - + "exception while applying " - + mCorrectionComponent.toString() - + " correction value [" + value + "]", e1 ); - } - } - } ); - } - } -} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerController.java b/src/source/tuner/fcd/proV1/FCD1TunerController.java index adac5c37..38e62608 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerController.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerController.java @@ -56,7 +56,7 @@ public class FCD1TunerController extends FCDTunerController private LNAEnhance mLNAEnhance; private MixerGain mMixerGain; private FCD1TunerConfiguration mTunerConfiguration; - private FCD1TunerEditorPanel mEditor; + private FCD1TunerEditor mEditor; public FCD1TunerController( Device device, DeviceDescriptor descriptor ) { diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java new file mode 100644 index 00000000..bd8ef493 --- /dev/null +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -0,0 +1,528 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source.tuner.fcd.proV1; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.text.DecimalFormat; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JSeparator; +import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceException; +import source.tuner.configuration.TunerConfiguration; +import source.tuner.configuration.TunerConfigurationEditor; +import source.tuner.configuration.TunerConfigurationEvent; +import source.tuner.configuration.TunerConfigurationEvent.Event; +import source.tuner.configuration.TunerConfigurationModel; +import source.tuner.fcd.FCDTuner; +import source.tuner.fcd.proV1.FCD1TunerController.LNAEnhance; +import source.tuner.fcd.proV1.FCD1TunerController.LNAGain; +import source.tuner.fcd.proV1.FCD1TunerController.MixerGain; + +public class FCD1TunerEditor extends TunerConfigurationEditor +{ + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( FCD1TunerEditor.class ); + + private JTextField mConfigurationName; + private JButton mTunerInfo; + private JSpinner mFrequencyCorrection; + private JComboBox mComboLNAGain; + private JComboBox mComboLNAEnhance; + private JComboBox mComboMixerGain; + private CorrectionSpinner mCorrectionDCI; + private CorrectionSpinner mCorrectionDCQ; + private CorrectionSpinner mCorrectionGain; + private CorrectionSpinner mCorrectionPhase; + private boolean mLoading; + + private FCD1TunerController mController; + + public FCD1TunerEditor( TunerConfigurationModel tunerConfigurationModel, FCDTuner tuner ) + { + super( tunerConfigurationModel ); + mController = (FCD1TunerController)tuner.getController(); + + init(); + } + + private FCD1TunerConfiguration getConfiguration() + { + if( hasItem() ) + { + return (FCD1TunerConfiguration)getItem(); + } + + return null; + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", + "[][][][][][][][][][][][grow]" ) ); + + add( new JLabel( "FCD Pro Tuner Configuration" ), "span,align center" ); + + mConfigurationName = new JTextField(); + mConfigurationName.setEnabled( false ); + mConfigurationName.addFocusListener( new FocusListener() + { + @Override + public void focusLost( FocusEvent e ) + { + save(); + } + @Override + public void focusGained( FocusEvent e ) {} + } ); + + add( new JLabel( "Name:" ) ); + add( mConfigurationName, "span 2" ); + + mTunerInfo = new JButton( "Tuner Info" ); + mTunerInfo.setEnabled( false ); + mTunerInfo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, getTunerInfo(), + "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); + } + } ); + add( mTunerInfo ); + + SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + mFrequencyCorrection.setEnabled( false ); + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + try + { + mController.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner " + + "Controller - couldn't apply frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "FuncubeDonglePro Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); + + add( new JLabel( "PPM:" ) ); + add( mFrequencyCorrection, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Gain" ), "span,align center" ); + + mComboLNAGain = new JComboBox( LNAGain.values() ); + mComboLNAGain.setEnabled( false ); + mComboLNAGain.setToolTipText( "Adjust the low noise amplifier gain setting." ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + LNAGain gain = (LNAGain)mComboLNAGain.getSelectedItem(); + + + try + { + mController.setLNAGain( gain ); + save(); + } + catch ( Exception e ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner Controller" + + " - error setting LNA gain [" + gain.toString() + "]" ); + + mLog.error( "FuncubeDonglePro Controller - error setting gain [" + gain.toString() + "]", e ); + } + } + } ); + + add( new JLabel( "LNA:" ) ); + add( mComboLNAGain ); + + /** + * LNA Enhance + */ + mComboLNAEnhance = new JComboBox( LNAEnhance.values() ); + mComboLNAEnhance.setToolTipText( "Adjust the LNA enhance setting. Default value is OFF" ); + + mComboLNAEnhance.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + LNAEnhance enhance = (LNAEnhance)mComboLNAEnhance.getSelectedItem(); + + try + { + mController.setLNAEnhance( enhance ); + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCD Pro Tuner" + + " error setting LNA enhance gain [" + enhance.toString() + "]" ); + + mLog.error( "FCDPro - error setting LNA enhance [" + enhance.toString() + "]", e1 ); + } + } + } ); + + add( new JLabel( "Enhance:" ) ); + add( mComboLNAEnhance ); + + /** + * Mixer Gain + */ + mComboMixerGain = new JComboBox( MixerGain.values() ); + mComboMixerGain.setToolTipText( "Adjust mixer gain setting" ); + + + mComboMixerGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + MixerGain gain = (MixerGain)mComboMixerGain.getSelectedItem(); + + try + { + mController.setMixerGain( gain ); + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error setting" + + " mixer gain [" + gain.toString() + "]" ); + + mLog.error( "FCDPro - error setting mixer gain [" + gain.toString() + "]", e1 ); + } + } + } ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixerGain, "wrap" ); + + add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" ); + add( new JLabel( "Correction" ), "span,align center" ); + + /** + * Inphase DC Correction + */ + mCorrectionDCI = new CorrectionSpinner( Correction.DC_INPHASE, 0.0, 0.00001, 5 ); + mCorrectionDCI.setEnabled( false ); + mCorrectionDCI.setToolTipText( "DC Bias Correction/Inphase " + + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "DC Inphase:" ) ); + add( mCorrectionDCI ); + + /** + * Quadrature DC Correction + */ + mCorrectionDCQ = new CorrectionSpinner( Correction.DC_QUADRATURE, 0.0, 0.00001, 5 ); + mCorrectionDCQ.setToolTipText( "DC Bias Correction/Quadrature " + + "Component: valid values are -1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "DC Quadrature:" ) ); + add( mCorrectionDCQ ); + + /** + * Gain Correction + */ + mCorrectionGain = new CorrectionSpinner( Correction.GAIN, 0.0, 0.00001, 5 ); + mCorrectionGain.setToolTipText( "Gain Correction: valid values are " + + "-1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "Gain:" ) ); + add( mCorrectionGain ); + + /** + * Phase Correction + */ + mCorrectionPhase = new CorrectionSpinner( Correction.PHASE, 0.0, 0.00001, 5 ); + mCorrectionPhase.setToolTipText( "Phase Correction: valid values are " + + "-1.0 to 1.0 (default: 0.0)" ); + add( new JLabel( "Phase:" ) ); + add( mCorrectionPhase ); + } + + /** + * Sets each of the tuner configuration controls to the enabled argument state + */ + private void setControlsEnabled( boolean enabled ) + { + if( mConfigurationName.isEnabled() != enabled ) + { + mConfigurationName.setEnabled( enabled ); + } + + if( mTunerInfo.isEnabled() != enabled ) + { + mTunerInfo.setEnabled( enabled ); + } + + if( mFrequencyCorrection.isEnabled() != enabled ) + { + mFrequencyCorrection.setEnabled( enabled ); + } + + if( mComboLNAEnhance.isEnabled() != enabled ) + { + mComboLNAEnhance.setEnabled( enabled ); + } + + if( mComboLNAGain.isEnabled() != enabled ) + { + mComboLNAGain.setEnabled( enabled ); + } + + if( mComboMixerGain.isEnabled() != enabled ) + { + mComboMixerGain.setEnabled( enabled ); + } + + if( mCorrectionDCI.isEnabled() != enabled ) + { + mCorrectionDCI.setEnabled( enabled ); + } + + if( mCorrectionDCQ.isEnabled() != enabled ) + { + mCorrectionDCQ.setEnabled( enabled ); + } + + if( mCorrectionGain.isEnabled() != enabled ) + { + mCorrectionGain.setEnabled( enabled ); + } + + if( mCorrectionPhase.isEnabled() != enabled ) + { + mCorrectionPhase.setEnabled( enabled ); + } + } + + private String getTunerInfo() + { + StringBuilder sb = new StringBuilder(); + + sb.append( "

    Funcube Dongle Pro Tuner

    " ); + + sb.append( "USB ID: " ); + sb.append( mController.getUSBID() ); + sb.append( "
    " ); + + sb.append( "USB Address: " ); + sb.append( mController.getUSBAddress() ); + sb.append( "
    " ); + + sb.append( "USB Speed: " ); + sb.append( mController.getUSBSpeed() ); + sb.append( "
    " ); + + sb.append( "Cellular Band: " ); + sb.append( mController.getConfiguration().getBandBlocking() ); + sb.append( "
    " ); + + sb.append( "Firmware: " ); + sb.append( mController.getConfiguration().getFirmware() ); + sb.append( "
    " ); + + return sb.toString(); + } + + @Override + public void save() + { + if( hasItem() && !mLoading ) + { + FCD1TunerConfiguration config = getConfiguration(); + + config.setName( mConfigurationName.getText() ); + + double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + config.setFrequencyCorrection( value ); + config.setLNAEnhance( (LNAEnhance)mComboLNAEnhance.getSelectedItem() ); + config.setLNAGain( (LNAGain)mComboLNAGain.getSelectedItem() ); + config.setMixerGain( (MixerGain)mComboMixerGain.getSelectedItem() ); + + double dci = ((SpinnerNumberModel)mCorrectionDCI.getModel()).getNumber().doubleValue(); + config.setInphaseDCCorrection( dci ); + + double dcq = ((SpinnerNumberModel)mCorrectionDCQ.getModel()).getNumber().doubleValue(); + config.setQuadratureDCCorrection( dcq ); + + double gain = ((SpinnerNumberModel)mCorrectionGain.getModel()).getNumber().doubleValue(); + config.setGainCorrection( gain ); + + double phase = ((SpinnerNumberModel)mCorrectionPhase.getModel()).getNumber().doubleValue(); + config.setPhaseCorrection( phase ); + + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + } + } + + @Override + public void setItem( TunerConfiguration tunerConfiguration ) + { + super.setItem( tunerConfiguration ); + + if( hasItem() ) + { + FCD1TunerConfiguration config = getConfiguration(); + + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + + if( tunerConfiguration.isAssigned() ) + { + setControlsEnabled( true ); + mConfigurationName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + mComboLNAEnhance.setSelectedItem( config.getLNAEnhance() ); + mComboLNAGain.setSelectedItem( config.getLNAGain() ); + mComboMixerGain.setSelectedItem( config.getMixerGain() ); + mCorrectionDCI.setValue( config.getInphaseDCCorrection() ); + mCorrectionDCQ.setValue( config.getQuadratureDCCorrection() ); + mCorrectionGain.setValue( config.getGainCorrection() ); + mCorrectionPhase.setValue( config.getPhaseCorrection() ); + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( config.getName() ); + } + + mLoading = false; + } + else + { + setControlsEnabled( false ); + mConfigurationName.setText( "" ); + } + } + + public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; + + public class CorrectionSpinner extends JSpinner + { + private static final long serialVersionUID = 1L; + private static final double MIN_VALUE = -1.0d; + private static final double MAX_VALUE = 1.0d; + + private Correction mCorrectionComponent; + + public CorrectionSpinner( Correction component, double initialValue, double step, int decimalPlaces ) + { + mCorrectionComponent = component; + + SpinnerModel model = new SpinnerNumberModel( initialValue, MIN_VALUE, MAX_VALUE, step ); + setModel( model ); + + JSpinner.NumberEditor editor = (JSpinner.NumberEditor)getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( decimalPlaces ); + + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + double value = + ((SpinnerNumberModel)getModel()).getNumber().doubleValue(); + + try + { + switch( mCorrectionComponent ) + { + case DC_INPHASE: + mController.setDCCorrectionInPhase( value ); + break; + case DC_QUADRATURE: + mController.setDCCorrectionQuadrature( value ); + break; + case GAIN: + mController.setGainCorrection( value ); + break; + case PHASE: + mController.setPhaseCorrection( value ); + break; + } + + save(); + } + catch ( Exception e1 ) + { + JOptionPane.showMessageDialog( FCD1TunerEditor.this, "FCDPro - error " + + "applying " + mCorrectionComponent.toString() + " correction value [" + + value + "]" ); + + mLog.error( "FCDPro - error applying "+ mCorrectionComponent.toString() + + " correction value [" + value + "]", e1 ); + } + } + } ); + } + } +} diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java b/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java deleted file mode 100644 index ff8321fd..00000000 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditorPanel.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.fcd.proV1; - -import gui.control.JFrequencyControl; - -import java.awt.Color; - -import javax.swing.JLabel; -import javax.swing.JPanel; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; -import source.tuner.fcd.FCDTuner; -import source.tuner.fcd.FCDTunerDetailsPanel; -import source.tuner.frequency.FrequencyChangeEvent; -import source.tuner.frequency.FrequencyChangeEvent.Event; -import source.tuner.frequency.IFrequencyChangeProcessor; - -import com.jidesoft.swing.JideTabbedPane; - -public class FCD1TunerEditorPanel extends JPanel implements IFrequencyChangeProcessor -{ - private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger( FCD1TunerEditorPanel.class ); - - private FCDTuner mTuner; - private FCD1TunerController mController; - private SettingsManager mSettingsManager; - - private JFrequencyControl mFrequencyControl; - - public FCD1TunerEditorPanel( FCDTuner tuner, SettingsManager settingsManager ) - { - mTuner = tuner; - mController = (FCD1TunerController)mTuner.getController(); - mSettingsManager = settingsManager; - - initGUI(); - } - - private void initGUI() - { - setLayout( new MigLayout( "fill,wrap 2", "[grow,right][grow]", "[][][grow]" ) ); - - add( new JLabel( "Funcube Dongle Pro Tuner" ), "span, align center" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.addListener( this ); - - /* Add frequency control as listener to tuner to get frequency changes - * that are invoked elsewhere, to keep the control in sync with the - * frequency */ - mTuner.getController().addListener( mFrequencyControl ); - - mFrequencyControl.setFrequency( mController.getFrequency(), false ); - - add( mFrequencyControl, "span,align center" ); - - JideTabbedPane tabs = new JideTabbedPane(); - tabs.setFont( this.getFont() ); - tabs.setForeground( Color.BLACK ); - - tabs.add( "Configuration", new FCD1TunerConfigurationPanel( - mSettingsManager, mController ) ); - - tabs.add( "Info", new FCDTunerDetailsPanel( mController ) ); - - add( tabs, "span,push,grow" ); - } - - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE ) - { - try - { - mController.setFrequency( event.getValue().longValue() ); - } - catch ( SourceException e ) - { - mLog.error( "FCTProController - error setting frequency [" + - event.getValue().longValue() + "]", e ); - } - } - } -} diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java b/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java deleted file mode 100644 index cdcaff7e..00000000 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerConfigurationPanel.java +++ /dev/null @@ -1,376 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.tuner.fcd.proplusV2; - -import java.awt.ComponentOrientation; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; -import java.text.DecimalFormat; - -import javax.swing.ComboBoxModel; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSpinner; -import javax.swing.JTextField; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import settings.SettingsManager; -import source.SourceException; - -public class FCD2TunerConfigurationPanel extends JPanel -{ - private final static Logger mLog = - LoggerFactory.getLogger( FCD2TunerConfigurationPanel.class ); - - private static final long serialVersionUID = 1L; - private SettingsManager mSettingsManager; - private FCD2TunerController mController; - private FCD2TunerConfiguration mSelectedConfig; - private JButton mNewConfiguration; - private JButton mDeleteConfiguration; - private JComboBox mComboConfigurations; - private JTextField mName; - private JCheckBox mLNAGain; - private JCheckBox mMixerGain; - private JSpinner mFrequencyCorrection; - - public FCD2TunerConfigurationPanel( SettingsManager settingsManager, - FCD2TunerController controller ) - { - mSettingsManager = settingsManager; - mController = controller; - - init(); - } - - private void init() - { - setLayout( new MigLayout( "fill,wrap 2", "[right][left]", "[][][][][][grow]" ) ); - - mComboConfigurations = new JComboBox(); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD2TunerConfiguration selected = - (FCD2TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - update( selected ); - } - } - }); - - add( new JLabel( "Config:" ) ); - add( mComboConfigurations, "growx,push" ); - - mName = new JTextField(); - - mName.addFocusListener( new FocusListener() - { - @Override - public void focusLost( FocusEvent e ) - { - mSelectedConfig.setName( mName.getText() ); - save(); - } - @Override - public void focusGained( FocusEvent e ) {} - } ); - - add( new JLabel( "Name" ) ); - add( mName, "growx,push" ); - - /** - * Frequency Correction - */ - - SpinnerModel model = - new SpinnerNumberModel( 0.0, //initial value - -1000.0, //min - 1000.0, //max - 0.1 ); //step - - mFrequencyCorrection = new JSpinner( model ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); - - DecimalFormat format = editor.getFormat(); - format.setMinimumFractionDigits( 1 ); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mFrequencyCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - double value = ((SpinnerNumberModel)mFrequencyCorrection - .getModel()).getNumber().doubleValue(); - - try - { - mController.setFrequencyCorrection( value ); - mSelectedConfig.setFrequencyCorrection( value ); - save(); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD2TunerConfigurationPanel.this, - "FCD Pro Plus Tuner Controller - couldn't " - + "apply frequency correction value: " + value + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " - + "frequency correction value: " + value, e1 ); - } - } - } ); - - add( new JLabel( "Correction PPM:" ) ); - add( mFrequencyCorrection, "growx,push" ); - - /** - * LNA Gain - */ - mLNAGain = new JCheckBox( "LNA Gain:" ); - mLNAGain.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - - mLNAGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - mSelectedConfig.setGainLNA( mLNAGain.isSelected() ); - - save(); - - update( mSelectedConfig ); - } - } ); - - add( mLNAGain ); - - /** - * Mixer Gain - */ - mMixerGain = new JCheckBox( "Mixer Gain:" ); - mMixerGain.setComponentOrientation( ComponentOrientation.RIGHT_TO_LEFT ); - - mMixerGain.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - mSelectedConfig.setGainMixer( mMixerGain.isSelected() ); - - save(); - - update( mSelectedConfig ); - } - } ); - - add( mMixerGain ); - - /** - * Lookup the save config and apply that config to update all of the - * controls - */ -// TunerConfigurationAssignment savedConfig = mSettingsManager -// .getSelectedTunerConfiguration( -// TunerType.FUNCUBE_DONGLE_PRO_PLUS, -// mController.getUSBAddress() ); -// -// if( savedConfig != null ) -// { -// mSelectedConfig = getNamedConfiguration( -// savedConfig.getTunerConfigurationName() ); -// } - - if( mSelectedConfig == null ) - { - mSelectedConfig = mComboConfigurations.getItemAt( 0 ); - -// //Store this config as the default for this tuner at this address -// mSettingsManager -// .setSelectedTunerConfiguration( -// TunerType.FUNCUBE_DONGLE_PRO_PLUS, -// mController.getUSBAddress(), mSelectedConfig ); - } - - mComboConfigurations.setSelectedItem( mSelectedConfig ); - - /** - * Create a new configuration - */ - mNewConfiguration = new JButton( "New" ); - - mNewConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { -// TunerConfiguration config = -// mSettingsManager -// .addNewTunerConfiguration( -// TunerType.FUNCUBE_DONGLE_PRO_PLUS, -// "New Configuration" ); - - mComboConfigurations.setModel( getModel() ); - -// mComboConfigurations.setSelectedItem( config ); - - repaint(); - } - } ); - - add( mNewConfiguration, "growx,push" ); - - /** - * Delete the currently selected configuration - */ - mDeleteConfiguration = new JButton( "Delete" ); - - mDeleteConfiguration.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - FCD2TunerConfiguration selected = - (FCD2TunerConfiguration)mComboConfigurations - .getItemAt( mComboConfigurations.getSelectedIndex() ); - - if( selected != null ) - { - int n = JOptionPane.showConfirmDialog( - FCD2TunerConfigurationPanel.this, - "Are you sure you want to delete '" - + selected.getName() + "'?", - "Are you sure?", - JOptionPane.YES_NO_OPTION ); - - if( n == JOptionPane.YES_OPTION ) - { -// mSettingsManager -// .deleteTunerConfiguration( selected ); - - mComboConfigurations.setModel( getModel() ); - - mComboConfigurations.setSelectedIndex( 0 ); - - repaint(); - } - } - } - } ); - - add( mDeleteConfiguration, "growx,push" ); - } - - private void update( FCD2TunerConfiguration config ) - { - mSelectedConfig = config; - - try - { - mController.apply( config ); - - mName.setText( config.getName() ); - mLNAGain.setSelected( config.getGainLNA() ); - mMixerGain.setSelected( config.getGainMixer() ); - mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); - -// mSettingsManager.setSelectedTunerConfiguration( -// TunerType.FUNCUBE_DONGLE_PRO_PLUS, -// mController.getUSBAddress(), config ); - } - catch ( SourceException e1 ) - { - JOptionPane.showMessageDialog( - FCD2TunerConfigurationPanel.this, - "FCD Pro Plus Tuner Controller - couldn't " - + "apply the tuner configuration settings - " + - e1.getLocalizedMessage() ); - - mLog.error( "FuncubeDongleProPlus Controller - couldn't apply " - + "config [" + config.getName() + "]", e1 ); - } - } - - private ComboBoxModel getModel() - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - -// for( TunerConfiguration config: configs ) -// { -// model.addElement( (FCD2TunerConfiguration)config ); -// } - - return model; - } - - private FCD2TunerConfiguration getNamedConfiguration( String name ) - { -// ArrayList configs = -// mSettingsManager -// .getTunerConfigurations( TunerType.FUNCUBE_DONGLE_PRO_PLUS ); - -// for( TunerConfiguration config: configs ) -// { -// if( config.getName().contentEquals( name ) ) -// { -// return (FCD2TunerConfiguration)config; -// } -// } - - return null; - } - - private void save() - { -// mSettingsManager.save(); - } -} From d93f497b331cee9370d6e15218e4f4aa7c24858d Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 20 Mar 2016 19:35:45 -0400 Subject: [PATCH 68/74] wip #34 - reformatting the channel configuration editors. --- src/controller/channel/Channel.java | 8 +- src/controller/channel/ChannelController.java | 30 ++- src/controller/channel/ChannelEditor.java | 252 ++++++++---------- .../channel/NameConfigurationEditor.java | 231 +++++++++------- src/gui/editor/EditorValidationException.java | 25 ++ src/gui/editor/EmptyValidatingEditor.java | 37 +++ src/gui/editor/ValidatingEditor.java | 15 ++ src/instrument/gui/DecoderSelectionFrame.java | 26 +- ...java => AuxDecodeConfigurationEditor.java} | 100 ++++--- src/module/decode/DecodeComponentEditor.java | 159 ----------- .../decode/DecodeConfigurationEditor.java | 129 +++++++++ src/module/decode/DecoderFactory.java | 149 +++++++++-- .../{AMEditor.java => AMDecoderEditor.java} | 28 +- .../decode/config/DecodeConfigFactory.java | 140 ---------- .../decode/ltrnet/LTRNetDecoderEditor.java | 156 +++++++++++ src/module/decode/ltrnet/LTRNetEditor.java | 71 ----- src/module/decode/p25/P25DecodeEditor.java | 4 +- ....java => EventLogConfigurationEditor.java} | 107 ++++++-- src/record/RecordComponentEditor.java | 99 ------- src/record/RecordConfigurationEditor.java | 152 +++++++++++ src/source/EmptySourceEditor.java | 61 ----- src/source/SourceComponentEditor.java | 134 ---------- src/source/SourceConfigurationEditor.java | 128 +++++++++ src/source/SourceEditor.java | 45 ---- src/source/SourceManager.java | 27 -- src/source/SourceType.java | 12 +- src/source/config/SourceConfigFactory.java | 10 +- src/source/config/SourceConfigTuner.java | 4 +- src/source/mixer/MixerEditor.java | 175 ------------ src/source/mixer/MixerSourceEditor.java | 155 +++++++++++ src/source/recording/RecordingEditor.java | 135 ---------- ...rEditorOld.java => TunerSourceEditor.java} | 83 +++--- 32 files changed, 1415 insertions(+), 1472 deletions(-) create mode 100644 src/gui/editor/EditorValidationException.java create mode 100644 src/gui/editor/EmptyValidatingEditor.java create mode 100644 src/gui/editor/ValidatingEditor.java rename src/module/decode/{AuxDecodeComponentEditor.java => AuxDecodeConfigurationEditor.java} (64%) delete mode 100644 src/module/decode/DecodeComponentEditor.java create mode 100644 src/module/decode/DecodeConfigurationEditor.java rename src/module/decode/am/{AMEditor.java => AMDecoderEditor.java} (66%) delete mode 100644 src/module/decode/config/DecodeConfigFactory.java create mode 100644 src/module/decode/ltrnet/LTRNetDecoderEditor.java delete mode 100644 src/module/decode/ltrnet/LTRNetEditor.java rename src/module/log/{EventLogComponentEditor.java => EventLogConfigurationEditor.java} (51%) delete mode 100644 src/record/RecordComponentEditor.java create mode 100644 src/record/RecordConfigurationEditor.java delete mode 100644 src/source/EmptySourceEditor.java delete mode 100644 src/source/SourceComponentEditor.java create mode 100644 src/source/SourceConfigurationEditor.java delete mode 100644 src/source/SourceEditor.java delete mode 100644 src/source/mixer/MixerEditor.java create mode 100644 src/source/mixer/MixerSourceEditor.java delete mode 100644 src/source/recording/RecordingEditor.java rename src/source/tuner/{TunerEditorOld.java => TunerSourceEditor.java} (51%) diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java index f9aca2c6..cd5a5c1f 100644 --- a/src/controller/channel/Channel.java +++ b/src/controller/channel/Channel.java @@ -23,9 +23,9 @@ import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; import javax.xml.bind.annotation.XmlTransient; +import module.decode.DecoderFactory; import module.decode.DecoderType; import module.decode.config.AuxDecodeConfiguration; -import module.decode.config.DecodeConfigFactory; import module.decode.config.DecodeConfiguration; import module.log.EventLogType; import module.log.config.EventLogConfiguration; @@ -42,7 +42,6 @@ import source.config.SourceConfigTuner; import source.config.SourceConfiguration; import source.tuner.TunerChannel; import source.tuner.TunerChannel.Type; - import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeEvent.Event; import source.tuner.frequency.IFrequencyChangeProcessor; @@ -61,8 +60,7 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor // Static unique channel identifier tracking private static int UNIQUE_ID = 0; - private DecodeConfiguration mDecodeConfiguration = - DecodeConfigFactory.getDefaultDecodeConfiguration(); + private DecodeConfiguration mDecodeConfiguration = DecoderFactory.getDefaultDecodeConfiguration(); private AuxDecodeConfiguration mAuxDecodeConfiguration = new AuxDecodeConfiguration(); private SourceConfiguration mSourceConfiguration = @@ -135,7 +133,7 @@ public class Channel extends Configuration implements IFrequencyChangeProcessor channel.setAuxDecodeConfiguration( aux ); - channel.setDecodeConfiguration( DecodeConfigFactory.copy( mDecodeConfiguration ) ); + channel.setDecodeConfiguration( DecoderFactory.copy( mDecodeConfiguration ) ); EventLogConfiguration log = new EventLogConfiguration(); diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java index c571adf7..99e764aa 100644 --- a/src/controller/channel/ChannelController.java +++ b/src/controller/channel/ChannelController.java @@ -2,6 +2,7 @@ package controller.channel; import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -23,7 +24,6 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import source.SourceManager; import alias.AliasModel; @@ -57,8 +57,7 @@ public class ChannelController extends JPanel { mChannelModel = channelModel; - mEditor = new ChannelEditor( channelModel, channelMapModel, - sourceManager, aliasModel ); + mEditor = new ChannelEditor( channelModel, channelMapModel, sourceManager, aliasModel ); mChannelModel.addListener( mEditor ); init(); @@ -66,9 +65,7 @@ public class ChannelController extends JPanel private void init() { - setLayout( new MigLayout( "insets 0 0 0 0", - "[grow,fill]", - "[grow,fill]") ); + setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]") ); //System Configuration View and Editor mChannelTable = new JTable( mChannelModel ); @@ -109,11 +106,22 @@ public class ChannelController extends JPanel listAndButtonsPanel.add( channelScroller, "wrap" ); listAndButtonsPanel.add( buttonsPanel ); - JideSplitPane splitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); + JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); splitPane.setDividerSize( 5 ); splitPane.setShowGripper( true ); - splitPane.add( listAndButtonsPanel ); - splitPane.add( mEditor ); + + //Shrink this guy so that he'll eventually be expanded to the scroller preferred size + listAndButtonsPanel.setPreferredSize( new Dimension( 10, 10 ) ); + + //Attempt to get a 60:40 vertical sizing preference + JScrollPane listScroller = new JScrollPane( listAndButtonsPanel ); + listScroller.setPreferredSize( new Dimension( 800, 60 ) ); + + JScrollPane editorScroller = new JScrollPane( mEditor ); + editorScroller.setPreferredSize( new Dimension( 800, 40 ) ); + + splitPane.add( listScroller ); + splitPane.add( editorScroller ); add( splitPane ); } @@ -134,14 +142,14 @@ public class ChannelController extends JPanel if( channel != null ) { - mEditor.setChannel( channel ); + mEditor.setItem( channel ); mCopyChannelButton.setEnabled( true ); mDeleteChannelButton.setEnabled( true ); } } else { - mEditor.setChannel( null ); + mEditor.setItem( null ); mCopyChannelButton.setEnabled( false ); mDeleteChannelButton.setEnabled( false ); } diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 79779120..ff42633f 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -17,28 +17,26 @@ ******************************************************************************/ package controller.channel; +import gui.editor.Editor; + import java.awt.Color; -import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JOptionPane; -import javax.swing.JPanel; -import javax.swing.JSeparator; -import module.decode.AuxDecodeComponentEditor; -import module.decode.DecodeComponentEditor; -import module.log.EventLogComponentEditor; +import module.decode.AuxDecodeConfigurationEditor; +import module.decode.DecodeConfigurationEditor; +import module.log.EventLogConfigurationEditor; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; -import record.RecordComponentEditor; -import source.SourceComponentEditor; +import record.RecordConfigurationEditor; +import source.SourceConfigurationEditor; import source.SourceManager; import alias.AliasModel; @@ -47,22 +45,19 @@ import com.jidesoft.swing.JideTabbedPane; import controller.channel.ChannelEvent.Event; import controller.channel.map.ChannelMapModel; -public class ChannelEditor extends JPanel - implements ActionListener, ChannelEventListener +public class ChannelEditor extends Editor implements ActionListener, ChannelEventListener { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( ChannelEditor.class ); private NameConfigurationEditor mNameConfigurationEditor; - private DecodeComponentEditor mDecodeEditor; - private AuxDecodeComponentEditor mAuxDecodeEditor; - private EventLogComponentEditor mEventLogEditor; - private RecordComponentEditor mRecordEditor; - private SourceComponentEditor mSourceEditor; + private SourceConfigurationEditor mSourceConfigurationEditor; + private DecodeConfigurationEditor mDecodeConfigurationEditor; + private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor; + private EventLogConfigurationEditor mEventLogConfiguraitonEditor; + private RecordConfigurationEditor mRecordConfigurationEditor; - private Channel mChannel; - private JButton mEnableButton = new JButton( "Enable" ); private JLabel mChannelName = new JLabel( "Channel:" ); @@ -77,30 +72,42 @@ public class ChannelEditor extends JPanel SourceManager sourceManager, AliasModel aliasModel ) { - super(); - mChannelModel = channelModel; mChannelMapModel = channelMapModel; mSourceManager = sourceManager; - - mNameConfigurationEditor = new NameConfigurationEditor( aliasModel, - mChannelModel ); + mNameConfigurationEditor = new NameConfigurationEditor( aliasModel, mChannelModel ); - initGUI(); + init(); } /** * Sets the channel configuration in each of the channel editor components * Note: this method must be invoked on the swing event dispatch thread */ - public void setChannel( final Channel channel ) + public void setItem( final Channel channel ) { - mNameConfigurationEditor.setConfiguration( channel ); - mSourceEditor.setConfiguration( channel ); - mDecodeEditor.setConfiguration( channel ); - mAuxDecodeEditor.setConfiguration( channel ); - mEventLogEditor.setConfiguration( channel ); - mRecordEditor.setConfiguration( channel ); + //Check if the current channel configuration has changed + if( hasItem() && + ( mNameConfigurationEditor.isModified() ) ) + { + int option = JOptionPane.showConfirmDialog( ChannelEditor.this, + "This channel configuration has changed. Do you want to save these changes?", + "Save Changes?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE ); + + if( option == JOptionPane.YES_OPTION ) + { + save(); + } + + setModified( false ); + } + + mNameConfigurationEditor.setItem( channel ); + mSourceConfigurationEditor.setItem( channel ); + mDecodeConfigurationEditor.setItem( channel ); + mAuxDecodeConfigurationEditor.setItem( channel ); + mEventLogConfiguraitonEditor.setItem( channel ); + mRecordConfigurationEditor.setItem( channel ); if( channel != null ) { @@ -116,77 +123,73 @@ public class ChannelEditor extends JPanel mEnableButton.setEnabled( false ); mEnableButton.setBackground( getBackground() ); } - - - mChannel = channel; } - private void initGUI() + private void init() { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[][][][][grow]" ) ); - mEnableButton.addActionListener( this ); - mEnableButton.setEnabled( false ); - add( mEnableButton, "growx,push" ); - add( mChannelName, "growx,push" ); + add( mChannelName, "wrap" ); - add( new JSeparator( JSeparator.HORIZONTAL ), "span,growx,push" ); JideTabbedPane tabs = new JideTabbedPane(); tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); + tabs.setTabTrailingComponent( mChannelName ); tabs.addTab( "Name/Alias", mNameConfigurationEditor ); - mSourceEditor = new SourceComponentEditor( mSourceManager ); - tabs.addTab( "Source", mSourceEditor ); + mSourceConfigurationEditor = new SourceConfigurationEditor( mSourceManager ); + tabs.addTab( "Source", mSourceConfigurationEditor ); - mDecodeEditor = new DecodeComponentEditor( mChannelMapModel ); - tabs.addTab( "Decoder", mDecodeEditor ); + mDecodeConfigurationEditor = new DecodeConfigurationEditor( mChannelMapModel ); + tabs.addTab( "Decoder", mDecodeConfigurationEditor ); - mAuxDecodeEditor = new AuxDecodeComponentEditor(); - tabs.addTab( "Aux Decoders", mAuxDecodeEditor ); + mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor(); + tabs.addTab( "Aux Decoders", mAuxDecodeConfigurationEditor ); - mEventLogEditor = new EventLogComponentEditor(); - tabs.addTab( "Logging", mEventLogEditor ); + mEventLogConfiguraitonEditor = new EventLogConfigurationEditor(); + tabs.addTab( "Logging", mEventLogConfiguraitonEditor ); - mRecordEditor = new RecordComponentEditor(); - tabs.addTab( "Recording", mRecordEditor ); + mRecordConfigurationEditor = new RecordConfigurationEditor(); + tabs.addTab( "Recording", mRecordConfigurationEditor ); + + add( tabs, "span" ); + + mEnableButton.addActionListener( this ); + mEnableButton.setEnabled( false ); + add( mEnableButton ); - add( tabs, "span,grow,push" ); - JButton btnSave = new JButton( "Save" ); btnSave.addActionListener( ChannelEditor.this ); - add( btnSave, "growx,push" ); + add( btnSave ); JButton btnReset = new JButton( "Reset" ); btnReset.addActionListener( ChannelEditor.this ); - add( btnReset, "growx,push" ); + add( btnReset ); } @Override public void channelChanged( ChannelEvent event ) { - if( mChannel != null && mChannel == event.getChannel() ) + if( hasItem() && getItem() == event.getChannel() ) { switch( event.getEvent() ) { case NOTIFICATION_CONFIGURATION_CHANGE: case NOTIFICATION_PROCESSING_START: case NOTIFICATION_PROCESSING_STOP: - setChannel( mChannel ); + setItem( getItem() ); mChannelEnableRequested = false; break; case NOTIFICATION_DELETE: - mChannel = null; - setChannel( mChannel ); + setItem( null ); break; case NOTIFICATION_ENABLE_REJECTED: if( mChannelEnableRequested ) { - JOptionPane.showMessageDialog( ChannelEditor.this, - "Channel could not be enabled. This is likely because " - + "there are no tuner channels available", "Couldn't " - + "enable channel", JOptionPane.INFORMATION_MESSAGE ); + JOptionPane.showMessageDialog( ChannelEditor.this, "Channel could not be " + + "enabled. This is likely because there are no tuner channels " + + "available", "Couldn't enable channel", JOptionPane.INFORMATION_MESSAGE ); mChannelEnableRequested = false; } @@ -195,7 +198,6 @@ public class ChannelEditor extends JPanel break; } } - } @Override @@ -205,17 +207,17 @@ public class ChannelEditor extends JPanel if( command.contentEquals( "Enable" ) ) { - if( mChannel != null ) + if( hasItem() ) { mChannelEnableRequested = true; - mChannelModel.broadcast( new ChannelEvent( mChannel, Event.REQUEST_ENABLE ) ); + mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_ENABLE ) ); } } else if( command.contentEquals( "Disable" ) ) { - if( mChannel != null ) + if( hasItem() ) { - mChannelModel.broadcast( new ChannelEvent( mChannel, Event.REQUEST_DISABLE ) ); + mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_DISABLE ) ); } } else if( command.contentEquals( "Save" ) ) @@ -224,90 +226,50 @@ public class ChannelEditor extends JPanel } else if( command.contentEquals( "Reset" ) ) { - setChannel( mChannel ); + setItem( getItem() ); } } public void save() { - try - { - //Validate each of the configuration editors to check for errors - mNameConfigurationEditor.validateConfiguration(); - mSourceEditor.validateConfiguration(); - mDecodeEditor.validateConfiguration(); - mAuxDecodeEditor.validateConfiguration(); - mEventLogEditor.validateConfiguration(); - - //Validate the source configuration against the decode editor to - //ensure that the source type is compatible with the decode type - - //Validate the aux decoders against the primary decoder -//TODO: make sure that no aux decoders are running with P25 - -// /** -// * Validate the source configuration against the other component -// * configuration editors -// */ -// validate( mSourceEditor.getSourceEditor() ); + if( hasItem() ) + { +// try +// { +//TODO: validate the source editor against the decode editor +//TODO: validate the aux editor against the decode editor - verify no aux decoders with P25 +// } +// catch( ConfigurationValidationException e ) +// { +// /* ChannelValidationException can be thrown by any of the component +// * editors. Show the validation error text in a popup menu to the +// * user and set the focus on the error component */ +// JOptionPane.showMessageDialog( ChannelEditor.this, +// validationException.getMessage(), +// "Channel Configuration Error", +// JOptionPane.ERROR_MESSAGE ); // -// mSourceEditor.save(); -// mDecodeEditor.save(); -// mAuxDecodeEditor.save(); -// mEventLogEditor.save(); -// mRecordEditor.save(); -// -// mChannel.setName( mChannelName.getText() ); -// mChannel.setEnabled( mChannelEnabled.isSelected() ); -// -// AliasList selected = mComboAliasLists.getItemAt( -// mComboAliasLists.getSelectedIndex() ); -// if( selected != null ) -// { -// mChannel.setAliasListName( selected.getName() ); -// } - } - catch ( ConfigurationValidationException validationException ) - { - /* ChannelValidationException can be thrown by any of the component - * editors. Show the validation error text in a popup menu to the - * user and set the focus on the error component */ - JOptionPane.showMessageDialog( ChannelEditor.this, - validationException.getMessage(), - "Channel Configuration Error", - JOptionPane.ERROR_MESSAGE ); +// Component component = validationException.getComponent(); +// +// if( component != null ) +// { +// component.requestFocus(); +// } +// +// return; +// } - Component component = validationException.getComponent(); - - if( component != null ) - { - component.requestFocus(); - } - - return; - } + mNameConfigurationEditor.save(); + mSourceConfigurationEditor.save(); + mDecodeConfigurationEditor.save(); + mAuxDecodeConfigurationEditor.save(); + mEventLogConfiguraitonEditor.save(); + mRecordConfigurationEditor.save(); - //Save the contents of each editor to the channel configuration - mNameConfigurationEditor.save(); - mSourceEditor.save(); - mDecodeEditor.save(); - mAuxDecodeEditor.save(); - mEventLogEditor.save(); - - //Broadcast a channel configuration change event - mChannelModel.broadcast( new ChannelEvent( mChannel, - Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); + //Broadcast a channel configuration change event + mChannelModel.broadcast( new ChannelEvent( getItem(), Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); + } + + setModified( false ); } -// /** -// * Validate the specified editor against each of the component editors -// */ -// @Override -// public void validate( ChannelConfigurationEditor editor ) throws ConfigurationValidationException -// { -// mSourceEditor.validate( editor ); -// mDecodeEditor.validate( editor ); -// mAuxDecodeEditor.validate( editor ); -// mEventLogEditor.validate( editor ); -// mRecordEditor.validate( editor ); -// } } diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java index f9b65bce..dcb6cd00 100644 --- a/src/controller/channel/NameConfigurationEditor.java +++ b/src/controller/channel/NameConfigurationEditor.java @@ -17,6 +17,8 @@ ******************************************************************************/ package controller.channel; +import gui.editor.Editor; + import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collections; @@ -24,43 +26,55 @@ import java.util.List; import javax.swing.ComboBoxModel; import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import net.miginfocom.swing.MigLayout; -import playlist.PlaylistManager; import alias.AliasModel; -public class NameConfigurationEditor extends ChannelConfigurationEditor +public class NameConfigurationEditor extends Editor implements DocumentListener { private static final long serialVersionUID = 1L; - private static final String DEFAULT_NAME = "Please select a channel to view/edit"; - + private final static Logger mLog = LoggerFactory.getLogger( NameConfigurationEditor.class ); + private static ComboBoxModel EMPTY_MODEL = new DefaultComboBoxModel<>(); private AliasModel mAliasModel; private ChannelModel mChannelModel; - private JTextField mChannelName = new JTextField( DEFAULT_NAME ); - private JComboBox mSystemNameCombo = new JComboBox<>( EMPTY_MODEL ); - private JComboBox mSiteNameCombo = new JComboBox<>( EMPTY_MODEL ); + private JTextField mChannelName; + private JComboBox mSystemNameCombo; + private JComboBox mSiteNameCombo; private JComboBox mAliasListCombo; - private Channel mChannel; - public NameConfigurationEditor( AliasModel aliasModel, ChannelModel model ) { mAliasModel = aliasModel; mChannelModel = model; - - setLayout( new MigLayout( "fill,wrap 2", "[right][]", "[][][][][][grow]" ) ); + + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][grow]" ) ); + + mChannelName = new JTextField( "" ); + mChannelName.getDocument().addDocumentListener( this ); + mChannelName.setEnabled( false ); add( new JLabel( "Name:" ) ); - add( mChannelName, "growx" ); - + add( mChannelName ); + + mSystemNameCombo = new JComboBox<>( EMPTY_MODEL ); + mSystemNameCombo.setEnabled( false ); mSystemNameCombo.setEditable( true ); mSystemNameCombo.addActionListener( new ActionListener() { @@ -75,9 +89,9 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor List sites = mChannelModel.getSites( system ); - if( !sites.contains( mChannel.getSite() ) ) + if( !sites.contains( getItem().getSite() ) ) { - sites.add( mChannel.getSite() ); + sites.add( getItem().getSite() ); } if( sites.isEmpty() ) @@ -90,110 +104,99 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor sites.toArray( new String[ sites.size() ] ) ) );; } - mSiteNameCombo.setSelectedItem( mChannel.getSite() ); + mSiteNameCombo.setSelectedItem( getItem().getSite() ); } + + setModified( true ); } } ); - + add( new JLabel( "System:" ) ); - add( mSystemNameCombo, "growx" ); - - mSiteNameCombo.setEditable( true ); - - add( new JLabel( "Site:" ) ); - add( mSiteNameCombo, "growx" ); - - /** - * ComboBox: Alias Lists - */ - add( new JLabel( "Alias List:" ) ); + add( mSystemNameCombo ); mAliasListCombo = new JComboBox(); - - List lists = mAliasModel.getListNames(); - - Collections.sort( lists ); + mAliasListCombo.setEnabled( false ); + mAliasListCombo.setEditable( true ); + mAliasListCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); - mAliasListCombo.setModel( new DefaultComboBoxModel( - lists.toArray( new String[ lists.size() ] ) ) ); + add( new JLabel( "Alias List:" ) ); + add( mAliasListCombo ); - add( mAliasListCombo, "growx" ); + mSiteNameCombo = new JComboBox<>( EMPTY_MODEL ); + mSiteNameCombo.setEnabled( false ); + mSiteNameCombo.setEditable( true ); + mSiteNameCombo.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); - add( new JLabel() ); - - final JButton aliasListButton = new JButton( "New Alias List ..." ); - -// aliasListButton.addActionListener( new ActionListener() -// { -// @Override -// public void actionPerformed( ActionEvent e ) -// { -// String name = JOptionPane.showInputDialog( aliasListButton, -// "Please enter an Alias List name:" ); -// -// if( name != null && !name.isEmpty() ) -// { -// AliasListOld aliasList = new AliasListOld( name ); -// -// mPlaylistManager.getPlayist().getAliasDirectory() -// .addAliasList( aliasList ); -// -// mPlaylistManager.save(); -// -// List lists = mPlaylistManager.getPlayist() -// .getAliasDirectory().getAliasList(); -// -// Collections.sort( lists ); -// -// mAliasListCombo.setModel( new DefaultComboBoxModel( -// lists.toArray( new AliasListOld[ lists.size() ] ) ) ); -// -// mAliasListCombo.setSelectedItem( aliasList ); -// } -// } -// } ); - - add( aliasListButton, "growx" ); + add( new JLabel( "Site:" ) ); + add( mSiteNameCombo ); } @Override public void save() { - if( mChannel != null ) + if( hasItem() && isModified() ) { - mChannel.setName( mChannelName.getText() ); + getItem().setName( mChannelName.getText() ); Object system = mSystemNameCombo.getSelectedItem(); - mChannel.setSystem( ( system == null ? null : (String)system ) ); + getItem().setSystem( ( system == null ? null : (String)system ) ); Object site = mSiteNameCombo.getSelectedItem(); - mChannel.setSite( ( site == null ? null : (String)site ) ); + getItem().setSite( ( site == null ? null : (String)site ) ); Object aliasList = mAliasListCombo.getSelectedItem(); - mChannel.setAliasListName( aliasList == null ? null : (String)aliasList ); + getItem().setAliasListName( aliasList == null ? null : (String)aliasList ); + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mChannelName.isEnabled() != enabled ) + { + mChannelName.setEnabled( enabled ); + } + + if( mAliasListCombo.isEnabled() != enabled ) + { + mAliasListCombo.setEnabled( enabled ); + } + + if( mSystemNameCombo.isEnabled() != enabled ) + { + mSystemNameCombo.setEnabled( enabled ); + } + + if( mSiteNameCombo.isEnabled() != enabled ) + { + mSiteNameCombo.setEnabled( enabled ); } } @Override - public void validateConfiguration() throws ConfigurationValidationException + public void setItem( Channel channel ) { - //System and site can be null, but we must (should?) have a channel name - String name = mChannelName.getText(); + super.setItem( channel ); - if( name == null || name.isEmpty() ) - { - throw new ConfigurationValidationException( - mChannelName, "Channel name cannot be empty" ); - } - } - - @Override - public void setConfiguration( Channel channel ) - { - mChannel = channel; - - if( mChannel != null ) + if( hasItem() ) { + setControlsEnabled( true ); + mChannelName.setText( channel.getName() ); List systems = mChannelModel.getSystems(); @@ -221,21 +224,43 @@ public class NameConfigurationEditor extends ChannelConfigurationEditor mSiteNameCombo.setModel( new DefaultComboBoxModel( sites.toArray( new String[ sites.size() ] ) ) );; } - - mSiteNameCombo.setSelectedItem( channel.getSite() ); - - String aliasListName = mChannel.getAliasListName(); - if( aliasListName != null ) - { - mAliasListCombo.setSelectedItem( aliasListName ); - } + if( channel.getSite() != null ) + { + mSiteNameCombo.setSelectedItem( channel.getSite() ); + } + + List lists = mAliasModel.getListNames(); + Collections.sort( lists ); + mAliasListCombo.setModel( new DefaultComboBoxModel( + lists.toArray( new String[ lists.size() ] ) ) ); + + if( channel.getAliasListName() != null ) + { + mAliasListCombo.setSelectedItem( channel.getAliasListName() ); + } } else { - mChannelName.setText( DEFAULT_NAME ); - mSystemNameCombo.setModel( EMPTY_MODEL ); - mSiteNameCombo.setModel( EMPTY_MODEL ); + setControlsEnabled( false ); + mChannelName.setText( null ); } + + setModified( false ); } + + @Override + public void insertUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void changedUpdate( DocumentEvent e ) + { + setModified( true ); + } + + @Override + public void removeUpdate( DocumentEvent e ) {} } diff --git a/src/gui/editor/EditorValidationException.java b/src/gui/editor/EditorValidationException.java new file mode 100644 index 00000000..00ccf251 --- /dev/null +++ b/src/gui/editor/EditorValidationException.java @@ -0,0 +1,25 @@ +package gui.editor; + +public class EditorValidationException extends Exception +{ + private static final long serialVersionUID = 1L; + + private Editor mEditor; + private String mReason; + + public EditorValidationException( Editor editor, String reason ) + { + mEditor = editor; + mReason = reason; + } + + public Editor getEditor() + { + return mEditor; + } + + public String getReason() + { + return mReason; + } +} diff --git a/src/gui/editor/EmptyValidatingEditor.java b/src/gui/editor/EmptyValidatingEditor.java new file mode 100644 index 00000000..5d8eda8a --- /dev/null +++ b/src/gui/editor/EmptyValidatingEditor.java @@ -0,0 +1,37 @@ +package gui.editor; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; + +public class EmptyValidatingEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + public EmptyValidatingEditor( String descriptor ) + { + setLayout( new MigLayout( "insets 0 0 0 0", "[center]", "[grow]" ) ); + add( new JLabel( "Please select " + descriptor ), "wrap" ); + } + + public EmptyValidatingEditor() + { + this( "an item" ); + } + + @Override + public void save() + { + } + + @Override + public void reset() + { + } + + @Override + public boolean isValid( Editor editor ) throws EditorValidationException + { + return true; + } +} diff --git a/src/gui/editor/ValidatingEditor.java b/src/gui/editor/ValidatingEditor.java new file mode 100644 index 00000000..30b13d72 --- /dev/null +++ b/src/gui/editor/ValidatingEditor.java @@ -0,0 +1,15 @@ +package gui.editor; + + +/** + * Editor class that allows other editors to be validated for configuration with this editor + */ +public abstract class ValidatingEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + /** + * Indicates if the editor argument is valid according to the settings of this editor + */ + public abstract boolean isValid( Editor editor ) throws EditorValidationException; +} diff --git a/src/instrument/gui/DecoderSelectionFrame.java b/src/instrument/gui/DecoderSelectionFrame.java index 94f1eb54..4ed935e9 100644 --- a/src/instrument/gui/DecoderSelectionFrame.java +++ b/src/instrument/gui/DecoderSelectionFrame.java @@ -8,7 +8,7 @@ import javax.swing.JButton; import javax.swing.JDesktopPane; import javax.swing.JInternalFrame; -import module.decode.DecodeComponentEditor; +import module.decode.DecodeConfigurationEditor; import module.decode.config.DecodeConfiguration; import net.miginfocom.swing.MigLayout; import playlist.PlaylistManager; @@ -22,7 +22,7 @@ public class DecoderSelectionFrame extends JInternalFrame { private static final long serialVersionUID = 1L; - private DecodeComponentEditor mDecodeEditor = new DecodeComponentEditor( null ); + private DecodeConfigurationEditor mDecodeEditor = new DecodeConfigurationEditor( null ); private PlaylistManager mPlaylistManager; private IControllableFileSource mSource; @@ -77,17 +77,17 @@ public class DecoderSelectionFrame extends JInternalFrame @Override public void actionPerformed( ActionEvent arg0 ) { - DecodeConfiguration config = mDecodeEditor.getDecodeConfig(); - - if( config != null ) - { - DecoderViewFrame decoderFrame = new DecoderViewFrame( - mPlaylistManager, null, mSource ); - - decoderFrame.setVisible( true ); - - mDesktop.add( decoderFrame ); - } +// DecodeConfiguration config = mDecodeEditor.getDecodeConfig(); +// +// if( config != null ) +// { +// DecoderViewFrame decoderFrame = new DecoderViewFrame( +// mPlaylistManager, null, mSource ); +// +// decoderFrame.setVisible( true ); +// +// mDesktop.add( decoderFrame ); +// } } } ); } diff --git a/src/module/decode/AuxDecodeComponentEditor.java b/src/module/decode/AuxDecodeConfigurationEditor.java similarity index 64% rename from src/module/decode/AuxDecodeComponentEditor.java rename to src/module/decode/AuxDecodeConfigurationEditor.java index 62e3c292..c217eea4 100644 --- a/src/module/decode/AuxDecodeComponentEditor.java +++ b/src/module/decode/AuxDecodeConfigurationEditor.java @@ -17,29 +17,34 @@ ******************************************************************************/ package module.decode; +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.swing.JCheckBox; -import net.miginfocom.swing.MigLayout; import module.decode.config.AuxDecodeConfiguration; +import net.miginfocom.swing.MigLayout; import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; -public class AuxDecodeComponentEditor extends ChannelConfigurationEditor +public class AuxDecodeConfigurationEditor extends Editor { private static final long serialVersionUID = 1L; private List mControls = new ArrayList<>(); - private Channel mChannel; - - public AuxDecodeComponentEditor() + public AuxDecodeConfigurationEditor() { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][][][][grow]" ) ); + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); List decoders = DecoderType.getAuxDecoders(); @@ -48,29 +53,26 @@ public class AuxDecodeComponentEditor extends ChannelConfigurationEditor for( DecoderType decoder: decoders ) { AuxDecoderCheckBox control = new AuxDecoderCheckBox( decoder ); - + control.setEnabled( false ); + control.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); add( control ); - mControls.add( control ); } } - public AuxDecodeConfiguration getConfig() - { - if( mChannel != null ) - { - return mChannel.getAuxDecodeConfiguration(); - } - - return new AuxDecodeConfiguration(); - } - @Override public void save() { - if( mChannel != null ) + if( hasItem() ) { - AuxDecodeConfiguration config = mChannel.getAuxDecodeConfiguration(); + AuxDecodeConfiguration config = getItem().getAuxDecodeConfiguration(); config.clearAuxDecoders(); @@ -82,7 +84,21 @@ public class AuxDecodeComponentEditor extends ChannelConfigurationEditor } } } + + setModified( false ); } + + private void setControlsEnabled( boolean enabled ) + { + for( AuxDecoderCheckBox box: mControls ) + { + if( box.isEnabled() != enabled ) + { + box.setEnabled( enabled ); + } + } + } + public class AuxDecoderCheckBox extends JCheckBox { @@ -104,27 +120,29 @@ public class AuxDecodeComponentEditor extends ChannelConfigurationEditor } @Override - public void setConfiguration( Channel channel ) + public void setItem( Channel channel ) { - mChannel = channel; - - for( AuxDecoderCheckBox checkBox: mControls ) + super.setItem( channel ); + + if( hasItem() ) { - if( mChannel != null && - mChannel.getAuxDecodeConfiguration().getAuxDecoders() - .contains( checkBox.getDecoderType() ) ) - { - checkBox.setSelected( true ); - } - else - { - checkBox.setSelected( false ); - } + setControlsEnabled( true ); + + for( AuxDecoderCheckBox cb: mControls ) + { + if( getItem().getAuxDecodeConfiguration().getAuxDecoders().contains( cb.getDecoderType() ) ) + { + cb.setSelected( true ); + } + else + { + cb.setSelected( false ); + } + } + } + else + { + setControlsEnabled( false ); } } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - } } diff --git a/src/module/decode/DecodeComponentEditor.java b/src/module/decode/DecodeComponentEditor.java deleted file mode 100644 index 5d460db1..00000000 --- a/src/module/decode/DecodeComponentEditor.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package module.decode; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import module.decode.config.DecodeConfigFactory; -import module.decode.config.DecodeConfiguration; -import net.miginfocom.swing.MigLayout; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; -import controller.channel.map.ChannelMapModel; - -public class DecodeComponentEditor extends ChannelConfigurationEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( DecodeComponentEditor.class ); - - private static final long serialVersionUID = 1L; - - private JComboBox mComboDecoders; - - private DecodeEditor mEditor; - - private Channel mChannel; - - public DecodeComponentEditor( final ChannelMapModel channelMapModel ) - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - - mComboDecoders = new JComboBox(); - - DefaultComboBoxModel model = - new DefaultComboBoxModel(); - - for( DecoderType type: DecoderType.getPrimaryDecoders() ) - { - model.addElement( type ); - } - - mComboDecoders.setModel( model ); - - mComboDecoders.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - DecoderType selected = mComboDecoders - .getItemAt( mComboDecoders.getSelectedIndex() ); - - if( selected != null ) - { - DecodeConfiguration config; - - if( mChannel == null || - mChannel.getDecodeConfiguration().getDecoderType() != selected ) - { - config = DecodeConfigFactory.getDecodeConfiguration( selected ); - } - else - { - config = mChannel.getDecodeConfiguration(); - } - - //Remove the existing one - if( mEditor != null ) - { - remove( mEditor ); - } - - //Change to the new one - mEditor = DecoderFactory.getEditorPanel( config, channelMapModel ); - - add( mEditor, "span 2,center" ); - - revalidate(); - repaint(); - } - } - }); - - add( new JLabel( "Decoder:" ) ); - add( mComboDecoders, "wrap" ); - } - - public DecodeConfiguration getDecodeConfig() - { - return mEditor.getConfig(); - } - - @Override - public void save() - { - if( mEditor != null ) - { - mEditor.save(); - } - - mChannel.setDecodeConfiguration( mEditor.getConfig() ); - } - -// /** -// * Validates the editor against the current decode editor -// */ -// @Override -// public void validate( ChannelConfigurationEditor editor ) throws ConfigurationValidationException -// { -// super.validate( editor ); -// -// mEditor.validate( editor ); -// } - - @Override - public void setConfiguration( Channel channel ) - { - mChannel = channel; - - if( mChannel != null ) - { - final DecoderType decoder = mChannel.getDecodeConfiguration().getDecoderType(); - - mComboDecoders.setSelectedItem( decoder ); - - mComboDecoders.requestFocus(); - - mComboDecoders.requestFocusInWindow(); - } - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub - } -} diff --git a/src/module/decode/DecodeConfigurationEditor.java b/src/module/decode/DecodeConfigurationEditor.java new file mode 100644 index 00000000..8fcf52e5 --- /dev/null +++ b/src/module/decode/DecodeConfigurationEditor.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.EmptyValidatingEditor; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import controller.channel.Channel; +import controller.channel.map.ChannelMapModel; + +public class DecodeConfigurationEditor extends ValidatingEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( DecodeConfigurationEditor.class ); + + private static final long serialVersionUID = 1L; + + private JComboBox mComboDecoders; + private ValidatingEditor mCurrentEditor = new EmptyValidatingEditor<>(); + + public DecodeConfigurationEditor( final ChannelMapModel channelMapModel ) + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", "[right][left][grow,fill]", "[][grow]" ) ); + + mComboDecoders = new JComboBox(); + mComboDecoders.setEnabled( false ); + + DefaultComboBoxModel model = new DefaultComboBoxModel(); + + for( DecoderType type: DecoderType.getPrimaryDecoders() ) + { + model.addElement( type ); + } + + mComboDecoders.setModel( model ); + mComboDecoders.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + DecoderType selected = mComboDecoders.getItemAt( mComboDecoders.getSelectedIndex() ); + setEditor( DecoderFactory.getEditor( selected ) ); + } + }); + + add( new JLabel( "Decoder:" ) ); + add( mComboDecoders ); + add( mCurrentEditor ); + } + + private void setEditor( ValidatingEditor editor ) + { + if( mCurrentEditor != editor ) + { + remove( mCurrentEditor ); + mCurrentEditor = editor; + add( mCurrentEditor ); + + revalidate(); + repaint(); + } + } + + @Override + public void save() + { +// if( mEditor != null ) +// { +// mEditor.save(); +// } +// +// mChannel.setDecodeConfiguration( mEditor.getConfig() ); + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + mComboDecoders.setEnabled( true ); + mCurrentEditor.setItem( channel ); + } + else + { + mComboDecoders.setEnabled( false ); + } + } + + @Override + public boolean isValid( Editor editor ) throws EditorValidationException + { + return mCurrentEditor.isValid( editor ); + } +} diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index c9304b62..d4caedc9 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -25,7 +25,9 @@ import message.Message; import message.MessageDirection; import module.Module; import module.decode.am.AMDecoder; +import module.decode.am.AMDecoderEditor; import module.decode.am.AMDecoderPanel; +import module.decode.am.DecodeConfigAM; import module.decode.config.AuxDecodeConfiguration; import module.decode.config.DecodeConfiguration; import module.decode.fleetsync2.Fleetsync2Decoder; @@ -38,32 +40,29 @@ import module.decode.lj1200.LJ1200DecoderState; import module.decode.lj1200.LJ1200MessageFilter; import module.decode.ltrnet.DecodeConfigLTRNet; import module.decode.ltrnet.LTRNetDecoder; +import module.decode.ltrnet.LTRNetDecoderEditor; import module.decode.ltrnet.LTRNetDecoderPanel; import module.decode.ltrnet.LTRNetDecoderState; -import module.decode.ltrnet.LTRNetEditor; import module.decode.ltrnet.LTRNetMessageFilter; import module.decode.ltrstandard.DecodeConfigLTRStandard; import module.decode.ltrstandard.LTRStandardDecoder; import module.decode.ltrstandard.LTRStandardDecoderPanel; import module.decode.ltrstandard.LTRStandardDecoderState; -import module.decode.ltrstandard.LTRStandardEditor; import module.decode.ltrstandard.LTRStandardMessageFilter; import module.decode.mdc1200.MDCDecoder; import module.decode.mdc1200.MDCDecoderPanel; import module.decode.mdc1200.MDCDecoderState; import module.decode.mdc1200.MDCMessageFilter; import module.decode.mpt1327.DecodeConfigMPT1327; -import module.decode.mpt1327.MPT1327ConfigEditor; import module.decode.mpt1327.MPT1327Decoder; import module.decode.mpt1327.MPT1327Decoder.Sync; import module.decode.mpt1327.MPT1327DecoderPanel; import module.decode.mpt1327.MPT1327DecoderState; import module.decode.mpt1327.MPT1327MessageFilter; +import module.decode.nbfm.DecodeConfigNBFM; import module.decode.nbfm.NBFMDecoder; import module.decode.nbfm.NBFMDecoderPanel; -import module.decode.nbfm.NBFMEditor; import module.decode.p25.DecodeConfigP25Phase1; -import module.decode.p25.P25DecodeEditor; import module.decode.p25.P25Decoder.Modulation; import module.decode.p25.P25DecoderPanel; import module.decode.p25.P25DecoderState; @@ -71,10 +70,10 @@ import module.decode.p25.P25_C4FMDecoder; import module.decode.p25.P25_LSMDecoder; import module.decode.p25.audio.P25AudioModule; import module.decode.p25.message.filter.P25MessageFilterSet; +import module.decode.passport.DecodeConfigPassport; import module.decode.passport.PassportDecoder; import module.decode.passport.PassportDecoderPanel; import module.decode.passport.PassportDecoderState; -import module.decode.passport.PassportEditor; import module.decode.passport.PassportMessageFilter; import module.decode.state.AlwaysUnsquelchedDecoderState; import module.decode.state.DecoderPanel; @@ -90,7 +89,6 @@ import module.demodulate.fm.FMDemodulatorModule; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import properties.SystemProperties; import record.RecorderType; import record.config.RecordConfiguration; @@ -108,6 +106,8 @@ import controller.channel.map.ChannelMapModel; import filter.AllPassFilter; import filter.FilterSet; import filter.IFilter; +import gui.editor.EmptyValidatingEditor; +import gui.editor.ValidatingEditor; public class DecoderFactory { @@ -432,41 +432,132 @@ public class DecoderFactory return filters; } - - public static DecodeEditor getEditorPanel( DecodeConfiguration config, - ChannelMapModel channelMapModel ) + + public static DecodeConfiguration getDefaultDecodeConfiguration() { - DecodeEditor configuredPanel; - - switch( config.getDecoderType() ) + return getDecodeConfiguration( DecoderType.NBFM ); + } + + public static DecodeConfiguration getDecodeConfiguration( DecoderType decoder ) + { + DecodeConfiguration retVal; + + switch( decoder ) { - case NBFM: - configuredPanel = new NBFMEditor( config ); - break; - case LTR_STANDARD: - configuredPanel = new LTRStandardEditor( config ); - break; + case AM: + return new DecodeConfigAM(); case LTR_NET: - configuredPanel = new LTRNetEditor( config ); + return new DecodeConfigLTRNet(); + case LTR_STANDARD: + return new DecodeConfigLTRStandard(); + case MPT1327: + return new DecodeConfigMPT1327(); + case NBFM: + return new DecodeConfigNBFM(); + case PASSPORT: + return new DecodeConfigPassport(); + case P25_PHASE1: + return new DecodeConfigP25Phase1(); + default: + throw new IllegalArgumentException( "DecodeConfigFactory - unknown decoder type [" + + decoder.toString() + "]" ); + } + } + + public static ValidatingEditor getEditor( DecoderType type ) + { + switch( type ) + { + case AM: + return new AMDecoderEditor(); + case LTR_NET: + return new LTRNetDecoderEditor(); + case LTR_STANDARD: break; case MPT1327: - configuredPanel = new MPT1327ConfigEditor( config, - channelMapModel ); break; - case PASSPORT: - configuredPanel = new PassportEditor( config ); + case NBFM: break; case P25_PHASE1: - configuredPanel = new P25DecodeEditor( config ); + break; + case PASSPORT: break; default: - configuredPanel = new DecodeEditor( config ); break; } - return configuredPanel; + return new EmptyValidatingEditor(); } - + + /** + * Creates a copy of the configuration + */ + public static DecodeConfiguration copy( DecodeConfiguration config ) + { + if( config != null ) + { + switch( config.getDecoderType() ) + { + case AM: + DecodeConfigAM originalAM = (DecodeConfigAM)config; + DecodeConfigAM copyAM = new DecodeConfigAM(); + copyAM.setAFC( originalAM.getAFC() ); + copyAM.setAFCMaximumCorrection( originalAM.getAFCMaximumCorrection() ); + return copyAM; + case LTR_NET: + DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet)config; + DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet(); + copyLTRNet.setAFC( originalLTRNet.getAFC() ); + copyLTRNet.setAFCMaximumCorrection( originalLTRNet.getAFCMaximumCorrection() ); + copyLTRNet.setMessageDirection( originalLTRNet.getMessageDirection() ); + return copyLTRNet; + case LTR_STANDARD: + DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard)config; + DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard(); + copyLTRStandard.setAFC( originalLTRStandard.getAFC() ); + copyLTRStandard.setAFCMaximumCorrection( originalLTRStandard.getAFCMaximumCorrection() ); + copyLTRStandard.setMessageDirection( originalLTRStandard.getMessageDirection() ); + return copyLTRStandard; + case MPT1327: + DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327)config; + DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327(); + copyMPT.setAFC( originalMPT.getAFC() ); + copyMPT.setAFCMaximumCorrection( originalMPT.getAFCMaximumCorrection() ); + copyMPT.setCallTimeout( originalMPT.getCallTimeout() ); + copyMPT.setChannelMapName( originalMPT.getChannelMapName() ); + copyMPT.setSync( originalMPT.getSync() ); + copyMPT.setTrafficChannelPoolSize( originalMPT.getTrafficChannelPoolSize() ); + return copyMPT; + case NBFM: + DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM)config; + DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM(); + copyNBFM.setAFC( originalNBFM.getAFC() ); + copyNBFM.setAFCMaximumCorrection( originalNBFM.getAFCMaximumCorrection() ); + return copyNBFM; + case P25_PHASE1: + DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1)config; + DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1(); + copyP25.setAFC( originalP25.getAFC() ); + copyP25.setAFCMaximumCorrection( originalP25.getAFCMaximumCorrection() ); + copyP25.setIgnoreDataCalls( originalP25.getIgnoreDataCalls() ); + copyP25.setModulation( originalP25.getModulation() ); + copyP25.setTrafficChannelPoolSize( originalP25.getTrafficChannelPoolSize() ); + return copyP25; + case PASSPORT: + DecodeConfigPassport originalPass = (DecodeConfigPassport)config; + DecodeConfigPassport copyPass = new DecodeConfigPassport(); + copyPass.setAFC( originalPass.getAFC() ); + copyPass.setAFCMaximumCorrection( originalPass.getAFCMaximumCorrection() ); + return copyPass; + default: + throw new IllegalArgumentException( "Unrecognized decoder " + + "configuration type:" + config.getDecoderType() ); + } + } + + return null; + } + public static String getRecordingFilename( String token ) { //Get the base recording filename diff --git a/src/module/decode/am/AMEditor.java b/src/module/decode/am/AMDecoderEditor.java similarity index 66% rename from src/module/decode/am/AMEditor.java rename to src/module/decode/am/AMDecoderEditor.java index 69561043..525e4cfc 100644 --- a/src/module/decode/am/AMEditor.java +++ b/src/module/decode/am/AMDecoderEditor.java @@ -17,15 +17,33 @@ ******************************************************************************/ package module.decode.am; -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; +import controller.channel.Channel; -public class AMEditor extends DecodeEditor +public class AMDecoderEditor extends ValidatingEditor { private static final long serialVersionUID = 1L; - public AMEditor( DecodeConfiguration config ) + public AMDecoderEditor() { - super( config ); + } + + @Override + public boolean isValid( Editor editor ) throws EditorValidationException + { + return true; + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + getItem().setDecodeConfiguration( new DecodeConfigAM() ); + } + + setModified( false ); } } diff --git a/src/module/decode/config/DecodeConfigFactory.java b/src/module/decode/config/DecodeConfigFactory.java deleted file mode 100644 index 8c0a0f0d..00000000 --- a/src/module/decode/config/DecodeConfigFactory.java +++ /dev/null @@ -1,140 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.config; - -import module.decode.DecoderType; -import module.decode.am.DecodeConfigAM; -import module.decode.ltrnet.DecodeConfigLTRNet; -import module.decode.ltrstandard.DecodeConfigLTRStandard; -import module.decode.mpt1327.DecodeConfigMPT1327; -import module.decode.nbfm.DecodeConfigNBFM; -import module.decode.p25.DecodeConfigP25Phase1; -import module.decode.passport.DecodeConfigPassport; - -public class DecodeConfigFactory -{ - public static DecodeConfiguration getDefaultDecodeConfiguration() - { - return getDecodeConfiguration( DecoderType.NBFM ); - } - - public static DecodeConfiguration - getDecodeConfiguration( DecoderType decoder ) - { - DecodeConfiguration retVal; - - switch( decoder ) - { - case AM: - retVal = new DecodeConfigAM(); - break; - case LTR_NET: - retVal = new DecodeConfigLTRNet(); - break; - case LTR_STANDARD: - retVal = new DecodeConfigLTRStandard(); - break; - case MPT1327: - retVal = new DecodeConfigMPT1327(); - break; - case NBFM: - retVal = new DecodeConfigNBFM(); - break; - case PASSPORT: - retVal = new DecodeConfigPassport(); - break; - case P25_PHASE1: - retVal = new DecodeConfigP25Phase1(); - break; - default: - throw new IllegalArgumentException( "DecodeConfigFactory - " - + "unknown decoder type [" + decoder.toString() + "]" ); - } - - return retVal; - } - - /** - * Creates a copy of the configuration - */ - public static DecodeConfiguration copy( DecodeConfiguration config ) - { - if( config != null ) - { - switch( config.getDecoderType() ) - { - case AM: - DecodeConfigAM originalAM = (DecodeConfigAM)config; - DecodeConfigAM copyAM = new DecodeConfigAM(); - copyAM.setAFC( originalAM.getAFC() ); - copyAM.setAFCMaximumCorrection( originalAM.getAFCMaximumCorrection() ); - return copyAM; - case LTR_NET: - DecodeConfigLTRNet originalLTRNet = (DecodeConfigLTRNet)config; - DecodeConfigLTRNet copyLTRNet = new DecodeConfigLTRNet(); - copyLTRNet.setAFC( originalLTRNet.getAFC() ); - copyLTRNet.setAFCMaximumCorrection( originalLTRNet.getAFCMaximumCorrection() ); - copyLTRNet.setMessageDirection( originalLTRNet.getMessageDirection() ); - return copyLTRNet; - case LTR_STANDARD: - DecodeConfigLTRStandard originalLTRStandard = (DecodeConfigLTRStandard)config; - DecodeConfigLTRStandard copyLTRStandard = new DecodeConfigLTRStandard(); - copyLTRStandard.setAFC( originalLTRStandard.getAFC() ); - copyLTRStandard.setAFCMaximumCorrection( originalLTRStandard.getAFCMaximumCorrection() ); - copyLTRStandard.setMessageDirection( originalLTRStandard.getMessageDirection() ); - return copyLTRStandard; - case MPT1327: - DecodeConfigMPT1327 originalMPT = (DecodeConfigMPT1327)config; - DecodeConfigMPT1327 copyMPT = new DecodeConfigMPT1327(); - copyMPT.setAFC( originalMPT.getAFC() ); - copyMPT.setAFCMaximumCorrection( originalMPT.getAFCMaximumCorrection() ); - copyMPT.setCallTimeout( originalMPT.getCallTimeout() ); - copyMPT.setChannelMapName( originalMPT.getChannelMapName() ); - copyMPT.setSync( originalMPT.getSync() ); - copyMPT.setTrafficChannelPoolSize( originalMPT.getTrafficChannelPoolSize() ); - return copyMPT; - case NBFM: - DecodeConfigNBFM originalNBFM = (DecodeConfigNBFM)config; - DecodeConfigNBFM copyNBFM = new DecodeConfigNBFM(); - copyNBFM.setAFC( originalNBFM.getAFC() ); - copyNBFM.setAFCMaximumCorrection( originalNBFM.getAFCMaximumCorrection() ); - return copyNBFM; - case P25_PHASE1: - DecodeConfigP25Phase1 originalP25 = (DecodeConfigP25Phase1)config; - DecodeConfigP25Phase1 copyP25 = new DecodeConfigP25Phase1(); - copyP25.setAFC( originalP25.getAFC() ); - copyP25.setAFCMaximumCorrection( originalP25.getAFCMaximumCorrection() ); - copyP25.setIgnoreDataCalls( originalP25.getIgnoreDataCalls() ); - copyP25.setModulation( originalP25.getModulation() ); - copyP25.setTrafficChannelPoolSize( originalP25.getTrafficChannelPoolSize() ); - return copyP25; - case PASSPORT: - DecodeConfigPassport originalPass = (DecodeConfigPassport)config; - DecodeConfigPassport copyPass = new DecodeConfigPassport(); - copyPass.setAFC( originalPass.getAFC() ); - copyPass.setAFCMaximumCorrection( originalPass.getAFCMaximumCorrection() ); - return copyPass; - default: - throw new IllegalArgumentException( "Unrecognized decoder " - + "configuration type:" + config.getDecoderType() ); - } - } - - return null; - } -} diff --git a/src/module/decode/ltrnet/LTRNetDecoderEditor.java b/src/module/decode/ltrnet/LTRNetDecoderEditor.java new file mode 100644 index 00000000..3f4e9c32 --- /dev/null +++ b/src/module/decode/ltrnet/LTRNetDecoderEditor.java @@ -0,0 +1,156 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode.ltrnet; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import message.MessageDirection; +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class LTRNetDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + protected JCheckBox mAFC; + protected JSpinner mAFCMaximumCorrection; + private JComboBox mComboDirection; + + public LTRNetDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 5", "[left][right][grow,fill][right][grow,fill]", + "[][grow]" ) ); + + mAFC = new JCheckBox( "AFC" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSpinner( new SpinnerNumberModel( 3000, 0, 15000, 1 ) ); + mAFCMaximumCorrection.setEnabled( false ); + JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mAFCMaximumCorrection.getEditor(); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + mAFCMaximumCorrection.setToolTipText( "Sets the maximum frequency correction value in hertz, 0 - 15kHz" ); + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Correction Limit Hz:" ) ); + add( mAFCMaximumCorrection ); + + mComboDirection = new JComboBox(); + mComboDirection.setModel( new DefaultComboBoxModel( MessageDirection.values() ) ); + mComboDirection.setEnabled( false ); + mComboDirection.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Format:" ) ); + add( mComboDirection ); + } + + @Override + public boolean isValid( Editor editor ) throws EditorValidationException + { + return true; + } + + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + mComboDirection.setEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigLTRNet ) + { + DecodeConfigLTRNet ltr = (DecodeConfigLTRNet)config; + mComboDirection.setSelectedItem( ltr.getMessageDirection() ); + mAFC.setSelected( ltr.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( ltr.getAFCMaximumCorrection() ); + } + } + else + { + mComboDirection.setEnabled( false ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigLTRNet ltr = new DecodeConfigLTRNet(); + ltr.setAFC( mAFC.isSelected() ); + + int value = ((SpinnerNumberModel)mAFCMaximumCorrection.getModel()).getNumber().intValue(); + ltr.setAFCMaximumCorrection( value ); + + ltr.setMessageDirection( (MessageDirection)mComboDirection.getSelectedItem() ); + } + + setModified( false ); + } +} diff --git a/src/module/decode/ltrnet/LTRNetEditor.java b/src/module/decode/ltrnet/LTRNetEditor.java deleted file mode 100644 index 7d7d920e..00000000 --- a/src/module/decode/ltrnet/LTRNetEditor.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.ltrnet; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import message.MessageDirection; -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class LTRNetEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboDirection; - - public LTRNetEditor( DecodeConfiguration config ) - { - super( config ); - - initGUI(); - } - - private void initGUI() - { - mComboDirection = new JComboBox(); - - mComboDirection.setModel( - new DefaultComboBoxModel( MessageDirection.values() ) ); - - mComboDirection.setSelectedItem( ((DecodeConfigLTRNet)mConfig).getMessageDirection() ); - - mComboDirection.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MessageDirection selected = mComboDirection - .getItemAt( mComboDirection.getSelectedIndex() ); - - if( selected != null ) - { - ((DecodeConfigLTRNet)mConfig).setMessageDirection( selected ); - } - } - } ); - - add( new JLabel( "Format:" ) ); - add( mComboDirection, "wrap" ); - } -} diff --git a/src/module/decode/p25/P25DecodeEditor.java b/src/module/decode/p25/P25DecodeEditor.java index ccdfb281..e381cc28 100644 --- a/src/module/decode/p25/P25DecodeEditor.java +++ b/src/module/decode/p25/P25DecodeEditor.java @@ -32,8 +32,8 @@ import module.decode.p25.P25Decoder.Modulation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import source.SourceEditor; -import source.tuner.TunerEditorOld; +import source.SourceConfigurationEditor; +import source.tuner.TunerSourceEditor; import controller.channel.ChannelConfigurationEditor; import controller.channel.ConfigurationValidationException; diff --git a/src/module/log/EventLogComponentEditor.java b/src/module/log/EventLogConfigurationEditor.java similarity index 51% rename from src/module/log/EventLogComponentEditor.java rename to src/module/log/EventLogConfigurationEditor.java index 07b7625e..9ecdf68e 100644 --- a/src/module/log/EventLogComponentEditor.java +++ b/src/module/log/EventLogConfigurationEditor.java @@ -17,34 +17,71 @@ ******************************************************************************/ package module.log; +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.util.List; import javax.swing.JCheckBox; -import net.miginfocom.swing.MigLayout; import module.log.config.EventLogConfiguration; +import net.miginfocom.swing.MigLayout; import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; -public class EventLogComponentEditor extends ChannelConfigurationEditor +public class EventLogConfigurationEditor extends Editor { private static final long serialVersionUID = 1L; - private JCheckBox mBinaryLogger = new JCheckBox( "Binary Messages" ); - private JCheckBox mDecodedLogger = new JCheckBox( "Decoded Messages" ); - private JCheckBox mCallEventLogger = new JCheckBox( "Call Events" ); - - private Channel mChannel; + private JCheckBox mBinaryLogger; + private JCheckBox mDecodedLogger; + private JCheckBox mCallEventLogger; - public EventLogComponentEditor() + public EventLogConfigurationEditor() { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][][][grow]" ) ); - - add( mBinaryLogger ); - add( mDecodedLogger ); - add( mCallEventLogger ); + init(); } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); + + mBinaryLogger = new JCheckBox( "Binary Messages" ); + mBinaryLogger.setEnabled( false ); + mBinaryLogger.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mBinaryLogger ); + + mDecodedLogger = new JCheckBox( "Decoded Messages" ); + mDecodedLogger.setEnabled( false ); + mDecodedLogger.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mDecodedLogger ); + + mCallEventLogger = new JCheckBox( "Call Events" ); + mCallEventLogger.setEnabled( false ); + mCallEventLogger.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mCallEventLogger ); + } /** * Saves the current configuration as the stored configuration @@ -52,9 +89,9 @@ public class EventLogComponentEditor extends ChannelConfigurationEditor @Override public void save() { - if( mChannel != null ) + if( hasItem() ) { - EventLogConfiguration config = mChannel.getEventLogConfiguration(); + EventLogConfiguration config = getItem().getEventLogConfiguration(); config.clear(); @@ -71,31 +108,45 @@ public class EventLogComponentEditor extends ChannelConfigurationEditor config.addLogger( EventLogType.CALL_EVENT ); } } + + setModified( false ); } @Override - public void setConfiguration( Channel channel ) + public void setItem( Channel channel ) { - mChannel = channel; + super.setItem( channel ); - if( mChannel != null ) + if( hasItem() ) { - List loggers = mChannel.getEventLogConfiguration().getLoggers(); - + setControlsEnabled( true ); + + List loggers = getItem().getEventLogConfiguration().getLoggers(); mBinaryLogger.setSelected( loggers.contains( EventLogType.BINARY_MESSAGE ) ); mDecodedLogger.setSelected( loggers.contains( EventLogType.DECODED_MESSAGE ) ); mCallEventLogger.setSelected( loggers.contains( EventLogType.CALL_EVENT ) ); } else { - mBinaryLogger.setSelected( false ); - mDecodedLogger.setSelected( false ); - mCallEventLogger.setSelected( false ); + setControlsEnabled( false ); } } - - @Override - public void validateConfiguration() throws ConfigurationValidationException + + private void setControlsEnabled( boolean enabled ) { + if( mBinaryLogger.isEnabled() != enabled ) + { + mBinaryLogger.setEnabled( enabled ); + } + + if( mDecodedLogger.isEnabled() != enabled ) + { + mDecodedLogger.setEnabled( enabled ); + } + + if( mCallEventLogger.isEnabled() != enabled ) + { + mCallEventLogger.setEnabled( enabled ); + } } } diff --git a/src/record/RecordComponentEditor.java b/src/record/RecordComponentEditor.java deleted file mode 100644 index 8b12c08a..00000000 --- a/src/record/RecordComponentEditor.java +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package record; - -import java.util.List; - -import javax.swing.JCheckBox; - -import net.miginfocom.swing.MigLayout; -import record.config.RecordConfiguration; -import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; - -public class RecordComponentEditor extends ChannelConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - private JCheckBox mAudioRecorder = new JCheckBox( "Audio" ); - private JCheckBox mBasebandRecorder = new JCheckBox( "Baseband I/Q" ); - private JCheckBox mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" ); - - private Channel mChannel; - - public RecordComponentEditor() - { - setLayout( new MigLayout( "fill,wrap 1", "[]", "[][][][grow]" ) ); - - add( mAudioRecorder ); - add( mBasebandRecorder ); - add( mTrafficBasebandRecorder ); - } - - @Override - public void save() - { - if( mChannel != null ) - { - RecordConfiguration config = mChannel.getRecordConfiguration(); - - config.clearRecorders(); - - if( mAudioRecorder.isSelected() ) - { - config.addRecorder( RecorderType.AUDIO ); - } - - if( mBasebandRecorder.isSelected() ) - { - config.addRecorder( RecorderType.BASEBAND ); - } - - if( mTrafficBasebandRecorder.isSelected() ) - { - config.addRecorder( RecorderType.TRAFFIC_BASEBAND ); - } - } - } - @Override - public void setConfiguration( Channel channel ) - { - mChannel = channel; - - if( mChannel != null ) - { - List recorders = mChannel.getRecordConfiguration().getRecorders(); - - mAudioRecorder.setSelected( recorders.contains( RecorderType.AUDIO ) ); - mBasebandRecorder.setSelected( recorders.contains( RecorderType.BASEBAND ) ); - mTrafficBasebandRecorder.setSelected( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) ); - } - else - { - mAudioRecorder.setSelected( false ); - mBasebandRecorder.setSelected( false ); - mTrafficBasebandRecorder.setSelected( false ); - } - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - } -} diff --git a/src/record/RecordConfigurationEditor.java b/src/record/RecordConfigurationEditor.java new file mode 100644 index 00000000..832f242e --- /dev/null +++ b/src/record/RecordConfigurationEditor.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package record; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.JCheckBox; + +import net.miginfocom.swing.MigLayout; +import record.config.RecordConfiguration; +import controller.channel.Channel; + +public class RecordConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAudioRecorder; + private JCheckBox mBasebandRecorder; + private JCheckBox mTrafficBasebandRecorder; + + public RecordConfigurationEditor() + { + + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); + + mAudioRecorder = new JCheckBox( "Audio" ); + mAudioRecorder.setEnabled( false ); + mAudioRecorder.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mAudioRecorder ); + + mBasebandRecorder = new JCheckBox( "Baseband I/Q" ); + mBasebandRecorder.setEnabled( false ); + mBasebandRecorder.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mBasebandRecorder ); + + mTrafficBasebandRecorder = new JCheckBox( "Traffic Channel Baseband I/Q" ); + mTrafficBasebandRecorder.setEnabled( false ); + mTrafficBasebandRecorder.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + add( mTrafficBasebandRecorder ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAudioRecorder.isEnabled() != enabled ) + { + mAudioRecorder.setEnabled( enabled ); + } + + if( mBasebandRecorder.isEnabled() != enabled ) + { + mBasebandRecorder.setEnabled( enabled ); + } + + if( mTrafficBasebandRecorder.isEnabled() != enabled ) + { + mTrafficBasebandRecorder.setEnabled( enabled ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + RecordConfiguration config = getItem().getRecordConfiguration(); + + config.clearRecorders(); + + if( mAudioRecorder.isSelected() ) + { + config.addRecorder( RecorderType.AUDIO ); + } + + if( mBasebandRecorder.isSelected() ) + { + config.addRecorder( RecorderType.BASEBAND ); + } + + if( mTrafficBasebandRecorder.isSelected() ) + { + config.addRecorder( RecorderType.TRAFFIC_BASEBAND ); + } + } + + setModified( false ); + } + + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + List recorders = getItem().getRecordConfiguration().getRecorders(); + mAudioRecorder.setSelected( recorders.contains( RecorderType.AUDIO ) ); + mBasebandRecorder.setSelected( recorders.contains( RecorderType.BASEBAND ) ); + mTrafficBasebandRecorder.setSelected( recorders.contains( RecorderType.TRAFFIC_BASEBAND ) ); + } + else + { + setControlsEnabled( false ); + } + } +} diff --git a/src/source/EmptySourceEditor.java b/src/source/EmptySourceEditor.java deleted file mode 100644 index 04af856d..00000000 --- a/src/source/EmptySourceEditor.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source; - -import javax.swing.JLabel; - -import controller.channel.Channel; -import controller.channel.ConfigurationValidationException; -import source.config.SourceConfiguration; - -public class EmptySourceEditor extends SourceEditor -{ - private static final long serialVersionUID = 1L; - - public EmptySourceEditor( SourceManager resourceManager, - SourceConfiguration config ) - { - super( resourceManager, config ); - - initGUI(); - } - - public void reset() {} - - public void save() {} - - private void initGUI() - { - JLabel selectLabel = new JLabel( "Please select a source" ); - add( selectLabel ); - } - - @Override - public void setConfiguration( Channel channel ) - { - // TODO Auto-generated method stub - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub - - } -} diff --git a/src/source/SourceComponentEditor.java b/src/source/SourceComponentEditor.java deleted file mode 100644 index faa5c78c..00000000 --- a/src/source/SourceComponentEditor.java +++ /dev/null @@ -1,134 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package source; - - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import net.miginfocom.swing.MigLayout; -import source.config.SourceConfigFactory; -import source.config.SourceConfiguration; -import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; - -public class SourceComponentEditor extends ChannelConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboSources; - private SourceEditor mEditor; - private SourceManager mSourceManager; - private Channel mChannel = null; - - public SourceComponentEditor( SourceManager sourceManager ) - { - mSourceManager = sourceManager; - - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][grow]" ) ); - - mComboSources = new JComboBox(); - mComboSources.setModel( new DefaultComboBoxModel( SourceType.values() ) ); - mComboSources.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - SourceType selected = mComboSources.getItemAt( mComboSources.getSelectedIndex() ); - - if( selected != null ) - { - SourceConfiguration config; - - if( mChannel == null || - mChannel.getSourceConfiguration().getSourceType() != selected ) - { - config = SourceConfigFactory.getSourceConfiguration( selected ); - } - else - { - config = mChannel.getSourceConfiguration(); - } - - //Remove the existing one - if( mEditor != null ) - { - remove( mEditor ); - } - - //Change to the new one - mEditor = mSourceManager.getPanel( config ); - - //Add it to the jpanel - add( mEditor, "span 2" ); - - revalidate(); - repaint(); - } - } - }); - - add( new JLabel( "Source:" ) ); - add( mComboSources, "wrap" ); - } - - public SourceEditor getSourceEditor() - { - return mEditor; - } - - @Override - public void save() - { - mEditor.save(); - - //Let the calling save method send the config change event - mChannel.setSourceConfiguration( mEditor.getConfig() ); - } - - /** - * Sets the channel configuration. Note: this method must be invoked from - * the swing event dispatch thread. - */ - @Override - public void setConfiguration( Channel channel ) - { - mChannel = channel; - - if( mChannel != null ) - { - mComboSources.setSelectedItem( - mChannel.getSourceConfiguration().getSourceType() ); - - mComboSources.requestFocus(); - - mComboSources.requestFocusInWindow(); - } - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - - } -} diff --git a/src/source/SourceConfigurationEditor.java b/src/source/SourceConfigurationEditor.java new file mode 100644 index 00000000..e5df0ae9 --- /dev/null +++ b/src/source/SourceConfigurationEditor.java @@ -0,0 +1,128 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source; + +import gui.editor.Editor; +import gui.editor.EmptyEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; +import source.mixer.MixerSourceEditor; +import source.tuner.TunerSourceEditor; +import controller.channel.Channel; + +public class SourceConfigurationEditor extends Editor +{ + private static final long serialVersionUID = 1L; + + private JComboBox mComboSources; + private MixerSourceEditor mMixerSourceEditor; + private TunerSourceEditor mTunerSourceEditor; + private Editor mCurrentEditor; + + public SourceConfigurationEditor( SourceManager sourceManager ) + { + mMixerSourceEditor = new MixerSourceEditor( sourceManager ); + mTunerSourceEditor = new TunerSourceEditor(); + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 3", "[right][left][grow,fill]", "[][grow]" ) ); + + mComboSources = new JComboBox(); + mComboSources.setModel( new DefaultComboBoxModel( SourceType.getTypes() ) ); + mComboSources.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + SourceType selected = (SourceType)mComboSources.getSelectedItem(); + + switch( selected ) + { + case MIXER: + setEditor( mMixerSourceEditor ); + break; + case TUNER: + setEditor( mTunerSourceEditor ); + break; + default: + setEditor( new EmptyEditor() ); + break; + } + } + }); + + add( new JLabel( "Source:" ) ); + add( mComboSources ); + + mCurrentEditor = mTunerSourceEditor; + add( mCurrentEditor ); + } + + private void setEditor( Editor editor ) + { + if( mCurrentEditor != editor ) + { + remove( mCurrentEditor ); + mCurrentEditor = editor; + add( mCurrentEditor ); + + revalidate(); + } + } + + @Override + public void save() + { + if( hasItem() && mCurrentEditor.isModified() ) + { + mCurrentEditor.save(); + } + } + + @Override + public boolean isModified() + { + return super.isModified() || mCurrentEditor.isModified(); + } + + /** + * Sets the channel configuration. Note: this method must be invoked from + * the swing event dispatch thread. + */ + @Override + public void setItem( Channel channel ) + { + super.setItem( channel ); + + if( hasItem() ) + { + mMixerSourceEditor.setItem( channel ); + mTunerSourceEditor.setItem( channel ); + } + } +} diff --git a/src/source/SourceEditor.java b/src/source/SourceEditor.java deleted file mode 100644 index 1cb15a64..00000000 --- a/src/source/SourceEditor.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source; - -import source.config.SourceConfiguration; -import controller.channel.ChannelConfigurationEditor; - -public abstract class SourceEditor extends ChannelConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - protected SourceManager mSourceManager; - protected SourceConfiguration mConfig; - - public SourceEditor( SourceManager sourceManager, SourceConfiguration config ) - { - mSourceManager = sourceManager; - mConfig = config; - } - - public SourceConfiguration getConfig() - { - return mConfig; - } - - protected SourceManager getSourceManager() - { - return mSourceManager; - } -} diff --git a/src/source/SourceManager.java b/src/source/SourceManager.java index 450a8b53..cc010d06 100644 --- a/src/source/SourceManager.java +++ b/src/source/SourceManager.java @@ -20,11 +20,8 @@ package source; import settings.SettingsManager; import source.config.SourceConfigTuner; import source.config.SourceConfiguration; -import source.mixer.MixerEditor; import source.mixer.MixerManager; -import source.recording.RecordingEditor; import source.recording.RecordingSourceManager; -import source.tuner.TunerEditorOld; import source.tuner.TunerManager; import source.tuner.TunerModel; import controller.ThreadPoolManager; @@ -86,28 +83,4 @@ public class SourceManager return retVal; } - - public SourceEditor getPanel( SourceConfiguration config ) - { - SourceEditor configuredPanel; - - switch( config.getSourceType() ) - { - case MIXER: - configuredPanel = new MixerEditor( this, config ); - break; - case TUNER: - configuredPanel = new TunerEditorOld( this, config ); - break; - case RECORDING: - configuredPanel = new RecordingEditor( this, config ); - break; - case NONE: - default: - configuredPanel = new EmptySourceEditor( this, config ); - break; - } - - return configuredPanel; - } } diff --git a/src/source/SourceType.java b/src/source/SourceType.java index be04d87c..421bdd91 100644 --- a/src/source/SourceType.java +++ b/src/source/SourceType.java @@ -17,10 +17,11 @@ ******************************************************************************/ package source; + public enum SourceType { NONE( "No Source" ), - MIXER( "Mixer/Sound Card" ), + MIXER( "Sound Card" ), TUNER( "Tuner" ), RECORDING( "IQ Recording" ); @@ -31,6 +32,15 @@ public enum SourceType mDisplayString = displayString; } + public static SourceType[] getTypes() + { + SourceType[] types = new SourceType[ 2 ]; + types[ 0 ] = SourceType.TUNER; + types[ 1 ] = SourceType.MIXER; + + return types; + } + public String getDisplayString() { return mDisplayString; diff --git a/src/source/config/SourceConfigFactory.java b/src/source/config/SourceConfigFactory.java index 6c271cb2..788518a1 100644 --- a/src/source/config/SourceConfigFactory.java +++ b/src/source/config/SourceConfigFactory.java @@ -26,21 +26,17 @@ public class SourceConfigFactory return getSourceConfiguration( SourceType.TUNER ); } - public static SourceConfiguration - getSourceConfiguration( SourceType source ) + public static SourceConfiguration getSourceConfiguration( SourceType source ) { SourceConfiguration retVal; switch( source ) { - case MIXER: - retVal = new SourceConfigMixer(); - break; case TUNER: retVal = new SourceConfigTuner(); break; - case RECORDING: - retVal = new SourceConfigRecording(); + case MIXER: + retVal = new SourceConfigMixer(); break; case NONE: default: diff --git a/src/source/config/SourceConfigTuner.java b/src/source/config/SourceConfigTuner.java index 61637d12..d6d9a8d4 100644 --- a/src/source/config/SourceConfigTuner.java +++ b/src/source/config/SourceConfigTuner.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -30,7 +30,7 @@ public class SourceConfigTuner extends SourceConfiguration private static DecimalFormat sFORMAT = new DecimalFormat( "0.00000" ); private long mFrequency = 0; - private int mBandwidth = 15000; + private int mBandwidth = 12500; public SourceConfigTuner() { diff --git a/src/source/mixer/MixerEditor.java b/src/source/mixer/MixerEditor.java deleted file mode 100644 index 075068b6..00000000 --- a/src/source/mixer/MixerEditor.java +++ /dev/null @@ -1,175 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.mixer; - -import java.util.EnumSet; - -import javax.sound.sampled.Mixer; -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import controller.channel.Channel; -import controller.channel.ConfigurationValidationException; -import source.SourceEditor; -import source.SourceManager; -import source.config.SourceConfigMixer; -import source.config.SourceConfiguration; -import source.mixer.MixerManager.InputMixerConfiguration; - -public class MixerEditor extends SourceEditor -{ - private static final long serialVersionUID = 1L; - private JComboBox mComboMixers; - private JComboBoxmComboChannels; - protected Mixer.Info mSelectedMixer = null; - - - public MixerEditor( SourceManager sourceManager, - SourceConfiguration config ) - { - super( sourceManager, config ); - - initGUI(); - } - - private void initGUI() - { - JLabel mixerLabel = new JLabel( "Mixer:" ); - add( mixerLabel, "align right" ); - - mComboMixers = new JComboBox(); - - add( mComboMixers, "wrap" ); - - resetMixers(); - - JLabel channelLabel = new JLabel( "Channel:" ); - add( channelLabel, "align right" ); - - mComboChannels = new JComboBox(); - - add( mComboChannels, "wrap" ); - - resetChannels(); - } - - public void reset() - { - resetMixers(); - resetChannels(); - } - - public void save() - { - SourceConfigMixer config = (SourceConfigMixer)mConfig; - - InputMixerConfiguration selectedMixer = - mComboMixers.getItemAt( mComboMixers.getSelectedIndex() ); - - if( selectedMixer != null ) - { - config.setMixer( selectedMixer.getMixerName() ); - } - - MixerChannel channel = mComboChannels.getItemAt( - mComboChannels.getSelectedIndex() ); - - if( channel != null ) - { - if( config.getChannel() != channel ) - { - config.setChannel( channel ); - } - } - } - - private void resetMixers() - { - - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - InputMixerConfiguration[] mixers = mSourceManager - .getMixerManager().getInputMixers(); - - mComboMixers.setModel( - new DefaultComboBoxModel( mixers ) ); - - SourceConfigMixer config = (SourceConfigMixer)mConfig; - - String mixer = config.getMixer(); - - if( mixer != null ) - { - mComboMixers.setSelectedItem( mixer ); - } - else - { - //Mixer either hasn't been set yet, or it was set to a - //mixer that is no longer available. Set the mixer to the - //first item, and then store the value - if( mixers.length > 0 ) - { - mComboMixers.setSelectedIndex( 0 ); - - InputMixerConfiguration selectedMixer = - (InputMixerConfiguration)mComboMixers.getSelectedItem(); - - config.setMixer( selectedMixer.getMixerName() ); - } - } - } - }); - } - - private void resetChannels() - { - javax.swing.SwingUtilities.invokeLater(new Runnable() - { - @Override - public void run() - { - EnumSet channels = MixerChannel.getTargetChannels(); - - mComboChannels.setModel( new DefaultComboBoxModel( - channels.toArray( new MixerChannel[ channels.size() ] ) ) ); - - MixerChannel channel = ((SourceConfigMixer)mConfig).getChannel(); - - mComboChannels.setSelectedItem( channel ); - } - }); - } - - @Override - public void setConfiguration( Channel channel ) - { - // TODO Auto-generated method stub - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub - - } -} diff --git a/src/source/mixer/MixerSourceEditor.java b/src/source/mixer/MixerSourceEditor.java new file mode 100644 index 00000000..fdae5537 --- /dev/null +++ b/src/source/mixer/MixerSourceEditor.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package source.mixer; + +import gui.editor.Editor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.EnumSet; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JComboBox; +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; +import source.SourceManager; +import source.config.SourceConfigMixer; +import source.config.SourceConfiguration; +import source.mixer.MixerManager.InputMixerConfiguration; +import controller.channel.Channel; + +public class MixerSourceEditor extends Editor +{ + private static final long serialVersionUID = 1L; + private JComboBox mComboMixers; + private JComboBoxmComboChannels; + + private SourceManager mSourceManager; + + public MixerSourceEditor( SourceManager sourceManager ) + { + mSourceManager = sourceManager; + init(); + } + + private void init() + { + setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][grow]" ) ); + + mComboMixers = new JComboBox(); + InputMixerConfiguration[] inputMixers = mSourceManager.getMixerManager().getInputMixers(); + mComboMixers.setModel( new DefaultComboBoxModel( inputMixers ) ); + mComboMixers.setEnabled( false ); + mComboMixers.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + InputMixerConfiguration selected = (InputMixerConfiguration)mComboMixers.getSelectedItem(); + + EnumSet channels = selected.getChannels(); + + mComboChannels.setModel( new DefaultComboBoxModel( + channels.toArray( new MixerChannel[ channels.size() ] ) ) ); + + setModified( true ); + } + } ); + + add( new JLabel( "Mixer:" ) ); + add( mComboMixers ); + + mComboChannels = new JComboBox(); + mComboChannels.setEnabled( false ); + mComboChannels.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Channel:" ) ); + add( mComboChannels ); + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + mComboMixers.setEnabled( true ); + mComboChannels.setEnabled( true ); + + SourceConfiguration config = item.getSourceConfiguration(); + + if( config instanceof SourceConfigMixer ) + { + SourceConfigMixer mixer = (SourceConfigMixer)config; + + for( InputMixerConfiguration inputMixer: mSourceManager.getMixerManager().getInputMixers() ) + { + if( inputMixer.getMixerName().equalsIgnoreCase( mixer.getMixer() ) ) + { + mComboMixers.setSelectedItem( inputMixer ); + + for( MixerChannel channel: inputMixer.getChannels() ) + { + if( channel.getLabel().equalsIgnoreCase( mixer.getChannel().getLabel() ) ) + { + mComboChannels.setSelectedItem( channel ); + } + } + + continue; + } + } + } + } + else + { + mComboMixers.setEnabled( false ); + mComboChannels.setEnabled( false ); + } + + setModified( false ); + } + + public void save() + { + if( hasItem() && isModified() ) + { + SourceConfigMixer config = new SourceConfigMixer(); + + InputMixerConfiguration selected = + (InputMixerConfiguration)mComboMixers.getSelectedItem(); + config.setMixer( selected.getMixerName() ); + + MixerChannel channel = (MixerChannel)mComboChannels.getSelectedItem(); + config.setChannel( channel ); + + getItem().setSourceConfiguration( config ); + } + + setModified( false ); + } +} diff --git a/src/source/recording/RecordingEditor.java b/src/source/recording/RecordingEditor.java deleted file mode 100644 index a6be5cbe..00000000 --- a/src/source/recording/RecordingEditor.java +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package source.recording; - -import gui.control.JFrequencyControl; - -import java.util.List; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; - -import controller.channel.Channel; -import controller.channel.ConfigurationValidationException; -import source.SourceEditor; -import source.SourceManager; -import source.config.SourceConfigRecording; -import source.config.SourceConfigTuner; -import source.config.SourceConfiguration; - -public class RecordingEditor extends SourceEditor -{ - private static final long serialVersionUID = 1L; - private JComboBox mComboRecordings; - private JFrequencyControl mFrequencyControl; - - public RecordingEditor( SourceManager sourceManager, SourceConfiguration config ) - { - super( sourceManager, config ); - - initGUI(); - } - - public void reset() - { - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); - } - - public void save() - { - SourceConfigRecording config = (SourceConfigRecording)mConfig; - - Recording selected = (Recording)mComboRecordings.getSelectedItem(); - - if( selected != null ) - { - config.setRecordingAlias( selected - .getRecordingConfiguration().getAlias() ); - } - - config.setFrequency( mFrequencyControl.getFrequency() ); - } - - private void initGUI() - { - add( new JLabel( "Recording" ) ); - - mComboRecordings = new JComboBox(); - - add( mComboRecordings, "wrap" ); - - mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.setFrequency( - ((SourceConfigRecording)mConfig).getFrequency(), false ); - - add( mFrequencyControl, "span" ); - - resetRecordings(); - } - - private void resetRecordings() - { - SwingUtilities.invokeLater( new Runnable() - { - @Override - public void run() - { - List recordings = getSourceManager() - .getRecordingSourceManager().getRecordings(); - - mComboRecordings.setModel( - new DefaultComboBoxModel( - recordings.toArray( new Recording[ recordings.size()] ) ) ); - - SourceConfigRecording config = (SourceConfigRecording)mConfig; - - String recordingAlias = config.getRecordingAlias(); - - if( recordingAlias != null ) - { - Recording selected = mSourceManager - .getRecordingSourceManager() - .getRecordingFromAlias( recordingAlias ); - - if( selected != null ) - { - mComboRecordings.setSelectedItem( selected ); - } - } - } - }); - } - - @Override - public void setConfiguration( Channel channel ) - { - // TODO Auto-generated method stub - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub - - } -} diff --git a/src/source/tuner/TunerEditorOld.java b/src/source/tuner/TunerSourceEditor.java similarity index 51% rename from src/source/tuner/TunerEditorOld.java rename to src/source/tuner/TunerSourceEditor.java index b17339e0..1bc26ee6 100644 --- a/src/source/tuner/TunerEditorOld.java +++ b/src/source/tuner/TunerSourceEditor.java @@ -17,59 +17,74 @@ ******************************************************************************/ package source.tuner; -import controller.channel.Channel; -import controller.channel.ConfigurationValidationException; import gui.control.JFrequencyControl; -import source.SourceEditor; -import source.SourceManager; +import gui.editor.Editor; +import net.miginfocom.swing.MigLayout; import source.config.SourceConfigTuner; import source.config.SourceConfiguration; +import source.tuner.frequency.FrequencyChangeEvent; +import source.tuner.frequency.IFrequencyChangeProcessor; +import controller.channel.Channel; -public class TunerEditorOld extends SourceEditor +public class TunerSourceEditor extends Editor { private static final long serialVersionUID = 1L; private JFrequencyControl mFrequencyControl; - - public TunerEditorOld( SourceManager sourceManager, - SourceConfiguration config ) + + public TunerSourceEditor() { - super( sourceManager, config ); - - initGUI(); + init(); } - public void reset() - { - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); - } - - public void save() - { - ((SourceConfigTuner)mConfig).setFrequency( mFrequencyControl.getFrequency() ); - } - - private void initGUI() + private void init() { + setLayout( new MigLayout( "fill", "[left]", "[][grow]" ) ); mFrequencyControl = new JFrequencyControl(); - - mFrequencyControl.setFrequency( - ((SourceConfigTuner)mConfig).getFrequency(), false ); - + mFrequencyControl.setEnabled( false ); + mFrequencyControl.addListener( new IFrequencyChangeProcessor() + { + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + setModified( true ); + } + } ); add( mFrequencyControl ); } - @Override - public void setConfiguration( Channel channel ) + public void save() { - // TODO Auto-generated method stub - - } + if( hasItem() && isModified() ) + { + SourceConfigTuner config = new SourceConfigTuner(); + config.setFrequency( mFrequencyControl.getFrequency() ); + getItem().setSourceConfiguration( config ); + } + setModified( false ); + } + @Override - public void validateConfiguration() throws ConfigurationValidationException + public void setItem( Channel item ) { - // TODO Auto-generated method stub + super.setItem( item ); + if( hasItem() ) + { + SourceConfiguration config = getItem().getSourceConfiguration(); + + if( config instanceof SourceConfigTuner ) + { + mFrequencyControl.setFrequency( ((SourceConfigTuner)config).getFrequency(), false ); + } + + mFrequencyControl.setEnabled( true ); + } + else + { + mFrequencyControl.setEnabled( false ); + } + + setModified( false ); } } From e89e7bd36144171a14f0e3eb1785c22406b7536e Mon Sep 17 00:00:00 2001 From: denny Date: Mon, 21 Mar 2016 18:30:19 -0400 Subject: [PATCH 69/74] wip #34 - updating the decoder editors to the new editor format --- src/controller/channel/ChannelEditor.java | 67 +++++++++---------- src/gui/editor/EmptyValidatingEditor.java | 3 +- src/gui/editor/ValidatingEditor.java | 2 +- .../decode/DecodeConfigurationEditor.java | 19 +++--- src/module/decode/am/AMDecoderEditor.java | 3 +- .../decode/ltrnet/LTRNetDecoderEditor.java | 8 ++- 6 files changed, 53 insertions(+), 49 deletions(-) diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index ff42633f..5c569db3 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -18,6 +18,7 @@ package controller.channel; import gui.editor.Editor; +import gui.editor.EditorValidationException; import java.awt.Color; import java.awt.event.ActionEvent; @@ -55,7 +56,7 @@ public class ChannelEditor extends Editor implements ActionListener, Ch private SourceConfigurationEditor mSourceConfigurationEditor; private DecodeConfigurationEditor mDecodeConfigurationEditor; private AuxDecodeConfigurationEditor mAuxDecodeConfigurationEditor; - private EventLogConfigurationEditor mEventLogConfiguraitonEditor; + private EventLogConfigurationEditor mEventLogConfigurationEditor; private RecordConfigurationEditor mRecordConfigurationEditor; private JButton mEnableButton = new JButton( "Enable" ); @@ -106,7 +107,7 @@ public class ChannelEditor extends Editor implements ActionListener, Ch mSourceConfigurationEditor.setItem( channel ); mDecodeConfigurationEditor.setItem( channel ); mAuxDecodeConfigurationEditor.setItem( channel ); - mEventLogConfiguraitonEditor.setItem( channel ); + mEventLogConfigurationEditor.setItem( channel ); mRecordConfigurationEditor.setItem( channel ); if( channel != null ) @@ -127,10 +128,8 @@ public class ChannelEditor extends Editor implements ActionListener, Ch private void init() { - setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[][][][][grow]" ) ); + setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]" ) ); - add( mChannelName, "wrap" ); - JideTabbedPane tabs = new JideTabbedPane(); tabs.setFont( this.getFont() ); tabs.setForeground( Color.BLACK ); @@ -147,8 +146,8 @@ public class ChannelEditor extends Editor implements ActionListener, Ch mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor(); tabs.addTab( "Aux Decoders", mAuxDecodeConfigurationEditor ); - mEventLogConfiguraitonEditor = new EventLogConfigurationEditor(); - tabs.addTab( "Logging", mEventLogConfiguraitonEditor ); + mEventLogConfigurationEditor = new EventLogConfigurationEditor(); + tabs.addTab( "Logging", mEventLogConfigurationEditor ); mRecordConfigurationEditor = new RecordConfigurationEditor(); tabs.addTab( "Recording", mRecordConfigurationEditor ); @@ -234,40 +233,40 @@ public class ChannelEditor extends Editor implements ActionListener, Ch { if( hasItem() ) { -// try -// { -//TODO: validate the source editor against the decode editor -//TODO: validate the aux editor against the decode editor - verify no aux decoders with P25 -// } -// catch( ConfigurationValidationException e ) -// { -// /* ChannelValidationException can be thrown by any of the component -// * editors. Show the validation error text in a popup menu to the -// * user and set the focus on the error component */ -// JOptionPane.showMessageDialog( ChannelEditor.this, -// validationException.getMessage(), -// "Channel Configuration Error", -// JOptionPane.ERROR_MESSAGE ); -// -// Component component = validationException.getComponent(); -// -// if( component != null ) -// { -// component.requestFocus(); -// } -// -// return; -// } + try + { + mDecodeConfigurationEditor.validate( mSourceConfigurationEditor ); + mDecodeConfigurationEditor.validate( mAuxDecodeConfigurationEditor ); + } + catch( EditorValidationException e ) + { + JOptionPane.showMessageDialog( ChannelEditor.this, e.getReason(), + "Configuration Error", JOptionPane.ERROR_MESSAGE ); + e.getEditor().requestFocus(); + + return; + } + + boolean changed = mNameConfigurationEditor.isModified() || + mSourceConfigurationEditor.isModified() || + mDecodeConfigurationEditor.isModified() || + mAuxDecodeConfigurationEditor.isModified() || + mEventLogConfigurationEditor.isModified() || + mRecordConfigurationEditor.isModified(); + mNameConfigurationEditor.save(); mSourceConfigurationEditor.save(); mDecodeConfigurationEditor.save(); mAuxDecodeConfigurationEditor.save(); - mEventLogConfiguraitonEditor.save(); + mEventLogConfigurationEditor.save(); mRecordConfigurationEditor.save(); - //Broadcast a channel configuration change event - mChannelModel.broadcast( new ChannelEvent( getItem(), Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); + if( changed ) + { + mChannelModel.broadcast( new ChannelEvent( getItem(), + Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); + } } setModified( false ); diff --git a/src/gui/editor/EmptyValidatingEditor.java b/src/gui/editor/EmptyValidatingEditor.java index 5d8eda8a..1c37c92c 100644 --- a/src/gui/editor/EmptyValidatingEditor.java +++ b/src/gui/editor/EmptyValidatingEditor.java @@ -30,8 +30,7 @@ public class EmptyValidatingEditor extends ValidatingEditor } @Override - public boolean isValid( Editor editor ) throws EditorValidationException + public void validate( Editor editor ) throws EditorValidationException { - return true; } } diff --git a/src/gui/editor/ValidatingEditor.java b/src/gui/editor/ValidatingEditor.java index 30b13d72..149a8c4f 100644 --- a/src/gui/editor/ValidatingEditor.java +++ b/src/gui/editor/ValidatingEditor.java @@ -11,5 +11,5 @@ public abstract class ValidatingEditor extends Editor /** * Indicates if the editor argument is valid according to the settings of this editor */ - public abstract boolean isValid( Editor editor ) throws EditorValidationException; + public abstract void validate( Editor editor ) throws EditorValidationException; } diff --git a/src/module/decode/DecodeConfigurationEditor.java b/src/module/decode/DecodeConfigurationEditor.java index 8fcf52e5..450a1288 100644 --- a/src/module/decode/DecodeConfigurationEditor.java +++ b/src/module/decode/DecodeConfigurationEditor.java @@ -87,6 +87,7 @@ public class DecodeConfigurationEditor extends ValidatingEditor { remove( mCurrentEditor ); mCurrentEditor = editor; + mCurrentEditor.setItem( getItem() ); add( mCurrentEditor ); revalidate(); @@ -97,12 +98,12 @@ public class DecodeConfigurationEditor extends ValidatingEditor @Override public void save() { -// if( mEditor != null ) -// { -// mEditor.save(); -// } -// -// mChannel.setDecodeConfiguration( mEditor.getConfig() ); + if( hasItem() && mCurrentEditor != null ) + { + mCurrentEditor.save(); + } + + setModified( false ); } @Override @@ -119,11 +120,13 @@ public class DecodeConfigurationEditor extends ValidatingEditor { mComboDecoders.setEnabled( false ); } + + setModified( false ); } @Override - public boolean isValid( Editor editor ) throws EditorValidationException + public void validate( Editor editor ) throws EditorValidationException { - return mCurrentEditor.isValid( editor ); + mCurrentEditor.validate( editor ); } } diff --git a/src/module/decode/am/AMDecoderEditor.java b/src/module/decode/am/AMDecoderEditor.java index 525e4cfc..53d1d41c 100644 --- a/src/module/decode/am/AMDecoderEditor.java +++ b/src/module/decode/am/AMDecoderEditor.java @@ -31,9 +31,8 @@ public class AMDecoderEditor extends ValidatingEditor } @Override - public boolean isValid( Editor editor ) throws EditorValidationException + public void validate( Editor editor ) throws EditorValidationException { - return true; } @Override diff --git a/src/module/decode/ltrnet/LTRNetDecoderEditor.java b/src/module/decode/ltrnet/LTRNetDecoderEditor.java index 3f4e9c32..fafdba63 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderEditor.java +++ b/src/module/decode/ltrnet/LTRNetDecoderEditor.java @@ -91,6 +91,7 @@ public class LTRNetDecoderEditor extends ValidatingEditor mComboDirection = new JComboBox(); mComboDirection.setModel( new DefaultComboBoxModel( MessageDirection.values() ) ); + mComboDirection.setSelectedItem( MessageDirection.OSW ); mComboDirection.setEnabled( false ); mComboDirection.addActionListener( new ActionListener() { @@ -106,9 +107,8 @@ public class LTRNetDecoderEditor extends ValidatingEditor } @Override - public boolean isValid( Editor editor ) throws EditorValidationException + public void validate( Editor editor ) throws EditorValidationException { - return true; } @@ -119,6 +119,8 @@ public class LTRNetDecoderEditor extends ValidatingEditor if( hasItem() ) { + mAFC.setEnabled( true ); + mAFCMaximumCorrection.setEnabled( true ); mComboDirection.setEnabled( true ); DecodeConfiguration config = getItem().getDecodeConfiguration(); @@ -133,6 +135,8 @@ public class LTRNetDecoderEditor extends ValidatingEditor } else { + mAFC.setEnabled( false ); + mAFCMaximumCorrection.setEnabled( false ); mComboDirection.setEnabled( false ); } } From 112083a22f0b9e94a8a01623b85b99146a009629 Mon Sep 17 00:00:00 2001 From: denny Date: Fri, 25 Mar 2016 14:26:08 -0400 Subject: [PATCH 70/74] wip #34 - finished updating all channel configuration editors and completed preliminary functional testing. --- src/alias/AliasController.java | 3 +- src/alias/MultipleAliasEditor.java | 2 +- .../channel/AbstractChannelEditor.java | 41 --- .../channel/ChannelConfigurationEditor.java | 44 --- src/controller/channel/ChannelController.java | 10 +- src/controller/channel/ChannelEditor.java | 119 ++++---- .../channel/NameConfigurationEditor.java | 5 +- src/gui/control/JFrequencyControl.java | 2 +- src/gui/editor/Editor.java | 51 +++- src/gui/editor/EmptyValidatingEditor.java | 2 +- .../decode/AuxDecodeConfigurationEditor.java | 5 + .../decode/DecodeConfigurationEditor.java | 25 +- src/module/decode/DecodeEditor.java | 127 -------- src/module/decode/DecoderFactory.java | 19 +- src/module/decode/am/AMDecoderEditor.java | 30 +- .../decode/config/DecodeConfiguration.java | 9 +- .../decode/ltrnet/LTRNetDecoderEditor.java | 71 +++-- .../ltrstandard/LTRStandardDecoderEditor.java | 177 +++++++++++ .../decode/ltrstandard/LTRStandardEditor.java | 71 ----- .../decode/mpt1327/MPT1327ConfigEditor.java | 219 ------------- .../decode/mpt1327/MPT1327DecoderEditor.java | 287 ++++++++++++++++++ src/module/decode/nbfm/NBFMDecoderEditor.java | 161 ++++++++++ src/module/decode/nbfm/NBFMEditor.java | 35 --- src/module/decode/p25/P25DecodeEditor.java | 147 --------- src/module/decode/p25/P25DecoderEditor.java | 226 ++++++++++++++ .../passport/PassportDecoderEditor.java | 159 ++++++++++ .../decode/passport/PassportEditor.java | 31 -- src/record/RecordEditor.java | 59 ---- src/source/SourceConfigurationEditor.java | 33 +- src/source/mixer/MixerSourceEditor.java | 32 +- src/source/tuner/TunerSourceEditor.java | 15 +- src/spectrum/OverlayPanel.java | 57 ++-- 32 files changed, 1328 insertions(+), 946 deletions(-) delete mode 100644 src/controller/channel/AbstractChannelEditor.java delete mode 100644 src/controller/channel/ChannelConfigurationEditor.java delete mode 100644 src/module/decode/DecodeEditor.java create mode 100644 src/module/decode/ltrstandard/LTRStandardDecoderEditor.java delete mode 100644 src/module/decode/ltrstandard/LTRStandardEditor.java delete mode 100644 src/module/decode/mpt1327/MPT1327ConfigEditor.java create mode 100644 src/module/decode/mpt1327/MPT1327DecoderEditor.java create mode 100644 src/module/decode/nbfm/NBFMDecoderEditor.java delete mode 100644 src/module/decode/nbfm/NBFMEditor.java delete mode 100644 src/module/decode/p25/P25DecodeEditor.java create mode 100644 src/module/decode/p25/P25DecoderEditor.java create mode 100644 src/module/decode/passport/PassportDecoderEditor.java delete mode 100644 src/module/decode/passport/PassportEditor.java delete mode 100644 src/record/RecordEditor.java diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index 5b8984a8..e6c86812 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -4,11 +4,11 @@ import gui.editor.Editor; import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import javax.swing.ImageIcon; @@ -148,6 +148,7 @@ public class AliasController extends JPanel ((Editor)component).setItem( null ); mSplitPane.remove( component ); + editor.setPreferredSize( new Dimension( 100, 100 ) ); mSplitPane.add( editor ); mSplitPane.validate(); diff --git a/src/alias/MultipleAliasEditor.java b/src/alias/MultipleAliasEditor.java index 0bf1f871..ae4eeeec 100644 --- a/src/alias/MultipleAliasEditor.java +++ b/src/alias/MultipleAliasEditor.java @@ -183,7 +183,7 @@ public class MultipleAliasEditor extends Editor> mPrioritySlider.setMajorTickSpacing( 20 ); mPrioritySlider.setMinorTickSpacing( 5 ); mPrioritySlider.setPaintTicks( true ); - mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 20, 20 ) ); + mPrioritySlider.setLabelTable( mPrioritySlider.createStandardLabels( 25, 25 ) ); mPrioritySlider.setPaintLabels( true ); mPrioritySlider.addChangeListener( new ChangeListener() { diff --git a/src/controller/channel/AbstractChannelEditor.java b/src/controller/channel/AbstractChannelEditor.java deleted file mode 100644 index 2ef02f60..00000000 --- a/src/controller/channel/AbstractChannelEditor.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package controller.channel; - - -public abstract class AbstractChannelEditor extends ChannelConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - protected Channel mChannel; - - public AbstractChannelEditor( Channel channel ) - { - super(); - mChannel = channel; - } - - public Channel getChannel() - { - return mChannel; - } - - public abstract void save(); - - public abstract void reset(); -} diff --git a/src/controller/channel/ChannelConfigurationEditor.java b/src/controller/channel/ChannelConfigurationEditor.java deleted file mode 100644 index 7b3e1427..00000000 --- a/src/controller/channel/ChannelConfigurationEditor.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - - * SDR Trunk - * Copyright (C) 2014-2016 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 - ******************************************************************************/ -package controller.channel; - -import javax.swing.JPanel; - -public abstract class ChannelConfigurationEditor extends JPanel -{ - private static final long serialVersionUID = 1L; - - /** - * Sets or resets the configuration editor components - */ - public abstract void setConfiguration( Channel channel ); - - /** - * Saves the contents of the editor components to the configuration - */ - public abstract void save(); - - /** - * Validates the contents of the editor components prior to saving the - * content to the configuration. - * - * @throws ConfigurationValidationException if the content is invalid - */ - public abstract void validateConfiguration() throws ConfigurationValidationException; -} diff --git a/src/controller/channel/ChannelController.java b/src/controller/channel/ChannelController.java index 99e764aa..17a9f8e2 100644 --- a/src/controller/channel/ChannelController.java +++ b/src/controller/channel/ChannelController.java @@ -85,17 +85,17 @@ public class ChannelController extends JPanel new MigLayout( "insets 0 0 0 0", "[grow,fill][grow,fill][grow,fill]", "[]") ); mNewChannelButton.addActionListener( this ); - mNewChannelButton.setToolTipText( "Adds a new default channel" ); + mNewChannelButton.setToolTipText( "Create a new channel configuration" ); buttonsPanel.add( mNewChannelButton ); mCopyChannelButton.addActionListener( this ); mCopyChannelButton.setEnabled( false ); - mCopyChannelButton.setToolTipText( "Creates a copy of the currently selected channel and adds it" ); + mCopyChannelButton.setToolTipText( "Create a copy of the currently selected channel configuration" ); buttonsPanel.add( mCopyChannelButton ); mDeleteChannelButton.addActionListener( this ); mDeleteChannelButton.setEnabled( false ); - mDeleteChannelButton.setToolTipText( "Deletes the currently selected channel" ); + mDeleteChannelButton.setToolTipText( "Delete the currently selected channel configuration" ); buttonsPanel.add( mDeleteChannelButton ); JPanel listAndButtonsPanel = new JPanel(); @@ -115,10 +115,10 @@ public class ChannelController extends JPanel //Attempt to get a 60:40 vertical sizing preference JScrollPane listScroller = new JScrollPane( listAndButtonsPanel ); - listScroller.setPreferredSize( new Dimension( 800, 60 ) ); + listScroller.setPreferredSize( new Dimension( 800, 58 ) ); JScrollPane editorScroller = new JScrollPane( mEditor ); - editorScroller.setPreferredSize( new Dimension( 800, 40 ) ); + editorScroller.setPreferredSize( new Dimension( 800, 42 ) ); splitPane.add( listScroller ); splitPane.add( editorScroller ); diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 5c569db3..42509675 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -81,51 +81,6 @@ public class ChannelEditor extends Editor implements ActionListener, Ch init(); } - /** - * Sets the channel configuration in each of the channel editor components - * Note: this method must be invoked on the swing event dispatch thread - */ - public void setItem( final Channel channel ) - { - //Check if the current channel configuration has changed - if( hasItem() && - ( mNameConfigurationEditor.isModified() ) ) - { - int option = JOptionPane.showConfirmDialog( ChannelEditor.this, - "This channel configuration has changed. Do you want to save these changes?", - "Save Changes?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE ); - - if( option == JOptionPane.YES_OPTION ) - { - save(); - } - - setModified( false ); - } - - mNameConfigurationEditor.setItem( channel ); - mSourceConfigurationEditor.setItem( channel ); - mDecodeConfigurationEditor.setItem( channel ); - mAuxDecodeConfigurationEditor.setItem( channel ); - mEventLogConfigurationEditor.setItem( channel ); - mRecordConfigurationEditor.setItem( channel ); - - if( channel != null ) - { - mChannelName.setText( "Channel: " + channel.getName() ); - mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" ); - mEnableButton.setEnabled( true ); - mEnableButton.setBackground( channel.getEnabled() ? Color.GREEN : getBackground() ); - } - else - { - mChannelName.setText( "Channel: " ); - mEnableButton.setText( "Enable" ); - mEnableButton.setEnabled( false ); - mEnableButton.setBackground( getBackground() ); - } - } - private void init() { setLayout( new MigLayout( "fill,wrap 3", "[grow,fill][grow,fill][grow,fill]", "[grow,fill][]" ) ); @@ -135,38 +90,48 @@ public class ChannelEditor extends Editor implements ActionListener, Ch tabs.setForeground( Color.BLACK ); tabs.setTabTrailingComponent( mChannelName ); + + mNameConfigurationEditor.setSaveRequestListener( this ); tabs.addTab( "Name/Alias", mNameConfigurationEditor ); mSourceConfigurationEditor = new SourceConfigurationEditor( mSourceManager ); + mSourceConfigurationEditor.setSaveRequestListener( this ); tabs.addTab( "Source", mSourceConfigurationEditor ); mDecodeConfigurationEditor = new DecodeConfigurationEditor( mChannelMapModel ); + mDecodeConfigurationEditor.setSaveRequestListener( this ); tabs.addTab( "Decoder", mDecodeConfigurationEditor ); mAuxDecodeConfigurationEditor = new AuxDecodeConfigurationEditor(); + mAuxDecodeConfigurationEditor.setSaveRequestListener( this ); tabs.addTab( "Aux Decoders", mAuxDecodeConfigurationEditor ); mEventLogConfigurationEditor = new EventLogConfigurationEditor(); + mEventLogConfigurationEditor.setSaveRequestListener( this ); tabs.addTab( "Logging", mEventLogConfigurationEditor ); mRecordConfigurationEditor = new RecordConfigurationEditor(); + mRecordConfigurationEditor.setSaveRequestListener( this ); tabs.addTab( "Recording", mRecordConfigurationEditor ); add( tabs, "span" ); mEnableButton.addActionListener( this ); mEnableButton.setEnabled( false ); + mEnableButton.setToolTipText( "Start the currently selected channel running/decoding" ); add( mEnableButton ); JButton btnSave = new JButton( "Save" ); + btnSave.setToolTipText( "Save changes to the currently selected channel" ); btnSave.addActionListener( ChannelEditor.this ); add( btnSave ); JButton btnReset = new JButton( "Reset" ); + btnReset.setToolTipText( "Reload the currently selected channel" ); btnReset.addActionListener( ChannelEditor.this ); add( btnReset ); } - + @Override public void channelChanged( ChannelEvent event ) { @@ -229,10 +194,48 @@ public class ChannelEditor extends Editor implements ActionListener, Ch } } + /** + * Sets the channel configuration in each of the channel editor components + * Note: this method must be invoked on the swing event dispatch thread + */ + public void setItem( final Channel channel ) + { + super.setItem( channel ); + + mNameConfigurationEditor.setItem( channel ); + mSourceConfigurationEditor.setItem( channel ); + mDecodeConfigurationEditor.setItem( channel ); + mAuxDecodeConfigurationEditor.setItem( channel ); + mEventLogConfigurationEditor.setItem( channel ); + mRecordConfigurationEditor.setItem( channel ); + + if( channel != null ) + { + mChannelName.setText( "Channel: " + channel.getName() ); + mEnableButton.setText( channel.getEnabled() ? "Disable" : "Enable" ); + mEnableButton.setEnabled( true ); + mEnableButton.setBackground( channel.getEnabled() ? Color.GREEN : getBackground() ); + } + else + { + mChannelName.setText( "Channel: " ); + mEnableButton.setText( "Enable" ); + mEnableButton.setEnabled( false ); + mEnableButton.setBackground( getBackground() ); + } + } + public void save() { if( hasItem() ) { + mNameConfigurationEditor.save(); + mSourceConfigurationEditor.save(); + mDecodeConfigurationEditor.save(); + mAuxDecodeConfigurationEditor.save(); + mEventLogConfigurationEditor.save(); + mRecordConfigurationEditor.save(); + try { mDecodeConfigurationEditor.validate( mSourceConfigurationEditor ); @@ -240,33 +243,17 @@ public class ChannelEditor extends Editor implements ActionListener, Ch } catch( EditorValidationException e ) { - JOptionPane.showMessageDialog( ChannelEditor.this, e.getReason(), - "Configuration Error", JOptionPane.ERROR_MESSAGE ); - e.getEditor().requestFocus(); + JOptionPane.showMessageDialog( e.getEditor(), e.getReason(), + "Configuration Error", JOptionPane.ERROR_MESSAGE ); + return; } - boolean changed = mNameConfigurationEditor.isModified() || - mSourceConfigurationEditor.isModified() || - mDecodeConfigurationEditor.isModified() || - mAuxDecodeConfigurationEditor.isModified() || - mEventLogConfigurationEditor.isModified() || - mRecordConfigurationEditor.isModified(); - - mNameConfigurationEditor.save(); - mSourceConfigurationEditor.save(); - mDecodeConfigurationEditor.save(); - mAuxDecodeConfigurationEditor.save(); - mEventLogConfigurationEditor.save(); - mRecordConfigurationEditor.save(); - if( changed ) - { - mChannelModel.broadcast( new ChannelEvent( getItem(), + mChannelModel.broadcast( new ChannelEvent( getItem(), Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); - } } setModified( false ); diff --git a/src/controller/channel/NameConfigurationEditor.java b/src/controller/channel/NameConfigurationEditor.java index dcb6cd00..e0525bdf 100644 --- a/src/controller/channel/NameConfigurationEditor.java +++ b/src/controller/channel/NameConfigurationEditor.java @@ -262,5 +262,8 @@ public class NameConfigurationEditor extends Editor implements Document } @Override - public void removeUpdate( DocumentEvent e ) {} + public void removeUpdate( DocumentEvent e ) + { + setModified( true ); + } } diff --git a/src/gui/control/JFrequencyControl.java b/src/gui/control/JFrequencyControl.java index 8e3b9802..d343e47e 100644 --- a/src/gui/control/JFrequencyControl.java +++ b/src/gui/control/JFrequencyControl.java @@ -78,7 +78,7 @@ public class JFrequencyControl extends JPanel implements IFrequencyChangeProcess private void init() { - setLayout( new MigLayout( "", "[]0[]", "" ) ); + setLayout( new MigLayout( "insets 0 0 0 0", "[]0[]", "" ) ); Font font = new Font( Font.MONOSPACED, Font.BOLD, 30 ); diff --git a/src/gui/editor/Editor.java b/src/gui/editor/Editor.java index 061080c0..01f76db1 100644 --- a/src/gui/editor/Editor.java +++ b/src/gui/editor/Editor.java @@ -6,10 +6,19 @@ import javax.swing.JPanel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import alias.id.AliasIdentifierEditor; - /** * Generic editor with change detection, save and reset functions. + * + * This editor supports hierarchical nesting of sub-component editors where changes are detected + * at the lowest editor in the tree and percolated up to the top-level editor for managing the + * configuration set as a whole. + * + * To configure this editor as a collective, register each parent editor as a saveRequestListener + * on each child editor. If no save request listener is registered, the editor will simply invoke + * save when a change is detected and the user opts to save the contents. + * + * The top-level editor can override the saveRequest() method to perform custom save operations + * and validation. */ public abstract class Editor extends JPanel { @@ -19,19 +28,51 @@ public abstract class Editor extends JPanel protected T mItem; protected boolean mModified = false; + protected Editor mSaveRequestListener; public Editor() { } + + /** + * Requests to save the contents of this editor. + * + * If a save request listener is registered, the listener is notified that a save is requested, + * otherwise the save is request is processed by the instance. + * + * This allows for a save event to be percolated up the hierarchical set of editors so that + * a top-level editor can invoke custom operations such as configuration validation. + */ + protected void saveRequest() + { + if( mSaveRequestListener != null ) + { + mSaveRequestListener.saveRequest(); + } + else + { + save(); + } + } + + /** + * Registers the editor to be notified when this editor contents have been saved. + */ + public void setSaveRequestListener( Editor listener ) + { + mSaveRequestListener = listener; + } + /** - * Sets the object to be edited + * Sets the object to be edited. If the contents from the previous object have changed, this + * prompts the user to save these changes. A saveRequest() is issued and either this editor + * or the parent editor will respond to the save request and invoke the save() method. */ public void setItem( T item ) { if( isModified() ) { - mLog.debug( "Editor that is modified: " + getClass() ); int option = JOptionPane.showConfirmDialog( Editor.this, "This item has changed. Do you want to save these changes?", @@ -41,7 +82,7 @@ public abstract class Editor extends JPanel if( option == JOptionPane.YES_OPTION ) { - save(); + saveRequest(); } setModified( false ); diff --git a/src/gui/editor/EmptyValidatingEditor.java b/src/gui/editor/EmptyValidatingEditor.java index 1c37c92c..cdcd438c 100644 --- a/src/gui/editor/EmptyValidatingEditor.java +++ b/src/gui/editor/EmptyValidatingEditor.java @@ -10,7 +10,7 @@ public class EmptyValidatingEditor extends ValidatingEditor public EmptyValidatingEditor( String descriptor ) { - setLayout( new MigLayout( "insets 0 0 0 0", "[center]", "[grow]" ) ); + setLayout( new MigLayout( "insets 0 0 0 0", "", "" ) ); add( new JLabel( "Please select " + descriptor ), "wrap" ); } diff --git a/src/module/decode/AuxDecodeConfigurationEditor.java b/src/module/decode/AuxDecodeConfigurationEditor.java index c217eea4..0e40834a 100644 --- a/src/module/decode/AuxDecodeConfigurationEditor.java +++ b/src/module/decode/AuxDecodeConfigurationEditor.java @@ -42,6 +42,11 @@ public class AuxDecodeConfigurationEditor extends Editor init(); } + public AuxDecodeConfiguration getConfiguration() + { + return hasItem() ? getItem().getAuxDecodeConfiguration() : null; + } + private void init() { setLayout( new MigLayout( "fill,wrap 4", "", "[][grow]" ) ); diff --git a/src/module/decode/DecodeConfigurationEditor.java b/src/module/decode/DecodeConfigurationEditor.java index 450a1288..a4d0fee0 100644 --- a/src/module/decode/DecodeConfigurationEditor.java +++ b/src/module/decode/DecodeConfigurationEditor.java @@ -27,7 +27,6 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; -import javax.swing.JLabel; import net.miginfocom.swing.MigLayout; @@ -39,21 +38,21 @@ import controller.channel.map.ChannelMapModel; public class DecodeConfigurationEditor extends ValidatingEditor { - private final static Logger mLog = LoggerFactory.getLogger( DecodeConfigurationEditor.class ); - private static final long serialVersionUID = 1L; private JComboBox mComboDecoders; - private ValidatingEditor mCurrentEditor = new EmptyValidatingEditor<>(); + private ValidatingEditor mCurrentEditor = new EmptyValidatingEditor<>( "a decoder" ); + private ChannelMapModel mChannelMapModel; public DecodeConfigurationEditor( final ChannelMapModel channelMapModel ) { + mChannelMapModel = channelMapModel; init(); } private void init() { - setLayout( new MigLayout( "fill,wrap 3", "[right][left][grow,fill]", "[][grow]" ) ); + setLayout( new MigLayout( "wrap 2", "[][grow,fill]", "[align top][grow,fill]" ) ); mComboDecoders = new JComboBox(); mComboDecoders.setEnabled( false ); @@ -72,11 +71,11 @@ public class DecodeConfigurationEditor extends ValidatingEditor public void actionPerformed( ActionEvent e ) { DecoderType selected = mComboDecoders.getItemAt( mComboDecoders.getSelectedIndex() ); - setEditor( DecoderFactory.getEditor( selected ) ); + ValidatingEditor editor = DecoderFactory.getEditor( selected, mChannelMapModel ); + setEditor( editor ); } }); - add( new JLabel( "Decoder:" ) ); add( mComboDecoders ); add( mCurrentEditor ); } @@ -85,9 +84,18 @@ public class DecodeConfigurationEditor extends ValidatingEditor { if( mCurrentEditor != editor ) { + //Set channel to null in current editor to force a save prompt as required + if( mCurrentEditor.isModified() ) + { + mCurrentEditor.setItem( null ); + } + remove( mCurrentEditor ); + mCurrentEditor = editor; + mCurrentEditor.setSaveRequestListener( this ); mCurrentEditor.setItem( getItem() ); + add( mCurrentEditor ); revalidate(); @@ -98,7 +106,7 @@ public class DecodeConfigurationEditor extends ValidatingEditor @Override public void save() { - if( hasItem() && mCurrentEditor != null ) + if( hasItem() ) { mCurrentEditor.save(); } @@ -114,6 +122,7 @@ public class DecodeConfigurationEditor extends ValidatingEditor if( hasItem() ) { mComboDecoders.setEnabled( true ); + mComboDecoders.setSelectedItem( channel.getDecodeConfiguration().getDecoderType() ); mCurrentEditor.setItem( channel ); } else diff --git a/src/module/decode/DecodeEditor.java b/src/module/decode/DecodeEditor.java deleted file mode 100644 index cddc6082..00000000 --- a/src/module/decode/DecodeEditor.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.JCheckBox; -import javax.swing.JLabel; -import javax.swing.JSpinner; -import javax.swing.SpinnerModel; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import module.decode.config.DecodeConfiguration; -import net.miginfocom.swing.MigLayout; -import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; - -public class DecodeEditor extends ChannelConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - protected DecodeConfiguration mConfig; - - protected JCheckBox mAFC; - protected JSpinner mAFCMaximumCorrection; - - public DecodeEditor( DecodeConfiguration config ) - { - setLayout( new MigLayout( "fill,wrap 2", "[right,grow][grow]", "[][][][][grow]" ) ); - - mConfig = config; - - if( config.supportsAFC() ) - { - mAFC = new JCheckBox( "Automatic Frequency Control" ); - mAFC.setSelected( mConfig.isAFCEnabled() ); - mAFC.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent arg0 ) - { - mConfig.setAFC( mAFC.isSelected() ); - } - } ); - - mAFC.setToolTipText( "AFC automatically adjusts the center " - + "frequency of the channel to correct/compensate for " - + "inaccuracies and frequency drift in the tuner" ); - - add( mAFC, "span" ); - - int initial = mConfig.getAFCMaximumCorrection(); - - SpinnerModel model = new SpinnerNumberModel( initial, 0, 15000, 1 ); - - mAFCMaximumCorrection = new JSpinner( model ); - - JSpinner.NumberEditor editor = - (JSpinner.NumberEditor)mAFCMaximumCorrection.getEditor(); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - - mAFCMaximumCorrection.setToolTipText( "Sets the maximum frequency " - + "correction value in hertz, 0 - 15kHz" ); - - mAFCMaximumCorrection.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - int value = ((SpinnerNumberModel)mAFCMaximumCorrection - .getModel()).getNumber().intValue(); - - mConfig.setAFCMaximumCorrection( value ); - - save(); - } - } ); - - add( new JLabel( "Correction Limit Hz:" ) ); - add( mAFCMaximumCorrection, "wrap" ); - } - } - - public DecodeConfiguration getConfig() - { - return mConfig; - } - - @Override - public void save() - { - } - - @Override - public void setConfiguration( Channel channel ) - { - // TODO Auto-generated method stub - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub - - } -} diff --git a/src/module/decode/DecoderFactory.java b/src/module/decode/DecoderFactory.java index d4caedc9..bdaf7e0d 100644 --- a/src/module/decode/DecoderFactory.java +++ b/src/module/decode/DecoderFactory.java @@ -48,6 +48,7 @@ import module.decode.ltrstandard.DecodeConfigLTRStandard; import module.decode.ltrstandard.LTRStandardDecoder; import module.decode.ltrstandard.LTRStandardDecoderPanel; import module.decode.ltrstandard.LTRStandardDecoderState; +import module.decode.ltrstandard.LTRStandardDecoderEditor; import module.decode.ltrstandard.LTRStandardMessageFilter; import module.decode.mdc1200.MDCDecoder; import module.decode.mdc1200.MDCDecoderPanel; @@ -56,14 +57,17 @@ import module.decode.mdc1200.MDCMessageFilter; import module.decode.mpt1327.DecodeConfigMPT1327; import module.decode.mpt1327.MPT1327Decoder; import module.decode.mpt1327.MPT1327Decoder.Sync; +import module.decode.mpt1327.MPT1327DecoderEditor; import module.decode.mpt1327.MPT1327DecoderPanel; import module.decode.mpt1327.MPT1327DecoderState; import module.decode.mpt1327.MPT1327MessageFilter; import module.decode.nbfm.DecodeConfigNBFM; import module.decode.nbfm.NBFMDecoder; import module.decode.nbfm.NBFMDecoderPanel; +import module.decode.nbfm.NBFMDecoderEditor; import module.decode.p25.DecodeConfigP25Phase1; import module.decode.p25.P25Decoder.Modulation; +import module.decode.p25.P25DecoderEditor; import module.decode.p25.P25DecoderPanel; import module.decode.p25.P25DecoderState; import module.decode.p25.P25_C4FMDecoder; @@ -74,6 +78,7 @@ import module.decode.passport.DecodeConfigPassport; import module.decode.passport.PassportDecoder; import module.decode.passport.PassportDecoderPanel; import module.decode.passport.PassportDecoderState; +import module.decode.passport.PassportDecoderEditor; import module.decode.passport.PassportMessageFilter; import module.decode.state.AlwaysUnsquelchedDecoderState; import module.decode.state.DecoderPanel; @@ -464,7 +469,7 @@ public class DecoderFactory } } - public static ValidatingEditor getEditor( DecoderType type ) + public static ValidatingEditor getEditor( DecoderType type, ChannelMapModel model ) { switch( type ) { @@ -473,20 +478,20 @@ public class DecoderFactory case LTR_NET: return new LTRNetDecoderEditor(); case LTR_STANDARD: - break; + return new LTRStandardDecoderEditor(); case MPT1327: - break; + return new MPT1327DecoderEditor( model ); case NBFM: - break; + return new NBFMDecoderEditor(); case P25_PHASE1: - break; + return new P25DecoderEditor(); case PASSPORT: - break; + return new PassportDecoderEditor(); default: break; } - return new EmptyValidatingEditor(); + return new EmptyValidatingEditor( "a decoder" ); } /** diff --git a/src/module/decode/am/AMDecoderEditor.java b/src/module/decode/am/AMDecoderEditor.java index 53d1d41c..697f7ebe 100644 --- a/src/module/decode/am/AMDecoderEditor.java +++ b/src/module/decode/am/AMDecoderEditor.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014-2016 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 @@ -20,6 +20,10 @@ package module.decode.am; import gui.editor.Editor; import gui.editor.EditorValidationException; import gui.editor.ValidatingEditor; + +import javax.swing.JLabel; + +import net.miginfocom.swing.MigLayout; import controller.channel.Channel; public class AMDecoderEditor extends ValidatingEditor @@ -28,6 +32,8 @@ public class AMDecoderEditor extends ValidatingEditor public AMDecoderEditor() { + setLayout( new MigLayout( "insets 0 0 0 0", "", "" ) ); + add( new JLabel( "AM Decoder" ) ); } @Override @@ -35,6 +41,28 @@ public class AMDecoderEditor extends ValidatingEditor { } + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + if( getItem().getDecodeConfiguration() instanceof DecodeConfigAM ) + { + setModified( false ); + } + else + { + setModified( true ); + } + } + else + { + setModified( false ); + } + } + @Override public void save() { diff --git a/src/module/decode/config/DecodeConfiguration.java b/src/module/decode/config/DecodeConfiguration.java index 95070a08..8390d608 100644 --- a/src/module/decode/config/DecodeConfiguration.java +++ b/src/module/decode/config/DecodeConfiguration.java @@ -17,7 +17,7 @@ ******************************************************************************/ package module.decode.config; -import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlSeeAlso; @@ -48,10 +48,12 @@ public abstract class DecodeConfiguration extends Configuration public static final int TRAFFIC_CHANNEL_LIMIT_DEFAULT = 3; public static final int TRAFFIC_CHANNEL_LIMIT_MINIMUM = 0; public static final int TRAFFIC_CHANNEL_LIMIT_MAXIMUM = 30; + + public static final int DEFAULT_AFC_MAX_CORRECTION = 1000; private DecoderType mDecoderType = DecoderType.NBFM; private boolean mAFCEnabled = true; - private int mAFCMaxCorrection = 3000; + private int mAFCMaxCorrection = DEFAULT_AFC_MAX_CORRECTION; public DecodeConfiguration() { @@ -68,7 +70,7 @@ public abstract class DecodeConfiguration extends Configuration return mDecoderType; } - @XmlElement( name = "afc" ) + @XmlAttribute( name = "afc" ) public boolean getAFC() { return mAFCEnabled; @@ -84,6 +86,7 @@ public abstract class DecodeConfiguration extends Configuration mAFCEnabled = enabled; } + @XmlAttribute( name = "AFCMaximumCorrection" ) public int getAFCMaximumCorrection() { return mAFCMaxCorrection; diff --git a/src/module/decode/ltrnet/LTRNetDecoderEditor.java b/src/module/decode/ltrnet/LTRNetDecoderEditor.java index fafdba63..1913412d 100644 --- a/src/module/decode/ltrnet/LTRNetDecoderEditor.java +++ b/src/module/decode/ltrnet/LTRNetDecoderEditor.java @@ -28,9 +28,7 @@ import javax.swing.DefaultComboBoxModel; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; -import javax.swing.JSpinner; -import javax.swing.SpinnerNumberModel; -import javax.swing.SwingConstants; +import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; @@ -43,8 +41,8 @@ public class LTRNetDecoderEditor extends ValidatingEditor { private static final long serialVersionUID = 1L; - protected JCheckBox mAFC; - protected JSpinner mAFCMaximumCorrection; + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; private JComboBox mComboDirection; public LTRNetDecoderEditor() @@ -54,10 +52,9 @@ public class LTRNetDecoderEditor extends ValidatingEditor private void init() { - setLayout( new MigLayout( "fill,wrap 5", "[left][right][grow,fill][right][grow,fill]", - "[][grow]" ) ); + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); - mAFC = new JCheckBox( "AFC" ); + mAFC = new JCheckBox( "AFC: 3000 Hz" ); mAFC.setEnabled( false ); mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + "correct/compensate for inaccuracies and frequency drift in the tuner" ); @@ -67,26 +64,36 @@ public class LTRNetDecoderEditor extends ValidatingEditor public void actionPerformed( ActionEvent arg0 ) { setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } } } ); add( mAFC ); - mAFCMaximumCorrection = new JSpinner( new SpinnerNumberModel( 3000, 0, 15000, 1 ) ); + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); mAFCMaximumCorrection.setEnabled( false ); - JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mAFCMaximumCorrection.getEditor(); - editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); - mAFCMaximumCorrection.setToolTipText( "Sets the maximum frequency correction value in hertz, 0 - 15kHz" ); - mAFCMaximumCorrection.addChangeListener( new ChangeListener() - { + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { @Override - public void stateChanged( ChangeEvent e ) - { + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); setModified( true ); - } - } ); - - add( new JLabel( "Correction Limit Hz:" ) ); + } + } ); add( mAFCMaximumCorrection ); mComboDirection = new JComboBox(); @@ -109,6 +116,7 @@ public class LTRNetDecoderEditor extends ValidatingEditor @Override public void validate( Editor editor ) throws EditorValidationException { + //No validation } @@ -120,7 +128,6 @@ public class LTRNetDecoderEditor extends ValidatingEditor if( hasItem() ) { mAFC.setEnabled( true ); - mAFCMaximumCorrection.setEnabled( true ); mComboDirection.setEnabled( true ); DecodeConfiguration config = getItem().getDecodeConfiguration(); @@ -131,13 +138,25 @@ public class LTRNetDecoderEditor extends ValidatingEditor mComboDirection.setSelectedItem( ltr.getMessageDirection() ); mAFC.setSelected( ltr.isAFCEnabled() ); mAFCMaximumCorrection.setValue( ltr.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( ltr.isAFCEnabled() ); + + setModified( false ); + } + else + { + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + + setModified( true ); } } else { mAFC.setEnabled( false ); - mAFCMaximumCorrection.setEnabled( false ); mComboDirection.setEnabled( false ); + setModified( false ); } } @@ -147,12 +166,12 @@ public class LTRNetDecoderEditor extends ValidatingEditor if( hasItem() && isModified() ) { DecodeConfigLTRNet ltr = new DecodeConfigLTRNet(); + ltr.setAFC( mAFC.isSelected() ); - - int value = ((SpinnerNumberModel)mAFCMaximumCorrection.getModel()).getNumber().intValue(); - ltr.setAFCMaximumCorrection( value ); - + ltr.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); ltr.setMessageDirection( (MessageDirection)mComboDirection.getSelectedItem() ); + + getItem().setDecodeConfiguration( ltr ); } setModified( false ); diff --git a/src/module/decode/ltrstandard/LTRStandardDecoderEditor.java b/src/module/decode/ltrstandard/LTRStandardDecoderEditor.java new file mode 100644 index 00000000..f75d38c5 --- /dev/null +++ b/src/module/decode/ltrstandard/LTRStandardDecoderEditor.java @@ -0,0 +1,177 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode.ltrstandard; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import message.MessageDirection; +import module.decode.config.DecodeConfiguration; +import module.decode.ltrnet.DecodeConfigLTRNet; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class LTRStandardDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + private JComboBox mComboDirection; + + public LTRStandardDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + + mComboDirection = new JComboBox(); + mComboDirection.setModel( new DefaultComboBoxModel( MessageDirection.values() ) ); + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mComboDirection.setEnabled( false ); + mComboDirection.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + setModified( true ); + } + } ); + + add( new JLabel( "Format:" ) ); + add( mComboDirection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + mAFC.setEnabled( true ); + mComboDirection.setEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigLTRStandard ) + { + DecodeConfigLTRStandard ltr = (DecodeConfigLTRStandard)config; + mComboDirection.setSelectedItem( ltr.getMessageDirection() ); + mAFC.setSelected( ltr.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( ltr.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( ltr.isAFCEnabled() ); + setModified( false ); + } + else + { + mComboDirection.setSelectedItem( MessageDirection.OSW ); + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + + setModified( true ); + } + } + else + { + mAFC.setEnabled( false ); + mComboDirection.setEnabled( false ); + setModified( false ); + } + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigLTRStandard ltr = new DecodeConfigLTRStandard(); + + ltr.setAFC( mAFC.isSelected() ); + ltr.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + ltr.setMessageDirection( (MessageDirection)mComboDirection.getSelectedItem() ); + + getItem().setDecodeConfiguration( ltr ); + } + + setModified( false ); + } +} diff --git a/src/module/decode/ltrstandard/LTRStandardEditor.java b/src/module/decode/ltrstandard/LTRStandardEditor.java deleted file mode 100644 index 35f0e107..00000000 --- a/src/module/decode/ltrstandard/LTRStandardEditor.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.ltrstandard; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JComboBox; -import javax.swing.JLabel; - -import message.MessageDirection; -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class LTRStandardEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboDirection; - - public LTRStandardEditor( DecodeConfiguration config ) - { - super( config ); - - initGUI(); - } - - private void initGUI() - { - mComboDirection = new JComboBox(); - - mComboDirection.setModel( - new DefaultComboBoxModel( MessageDirection.values() ) ); - - mComboDirection.setSelectedItem( ((DecodeConfigLTRStandard)mConfig).getMessageDirection() ); - - mComboDirection.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - MessageDirection selected = mComboDirection - .getItemAt( mComboDirection.getSelectedIndex() ); - - if( selected != null ) - { - ((DecodeConfigLTRStandard)mConfig).setMessageDirection( selected ); - } - } - } ); - - add( new JLabel( "Format:" ) ); - add( mComboDirection, "wrap" ); - } -} diff --git a/src/module/decode/mpt1327/MPT1327ConfigEditor.java b/src/module/decode/mpt1327/MPT1327ConfigEditor.java deleted file mode 100644 index 586a605d..00000000 --- a/src/module/decode/mpt1327/MPT1327ConfigEditor.java +++ /dev/null @@ -1,219 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.mpt1327; - -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JButton; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; -import module.decode.mpt1327.MPT1327Decoder.Sync; -import controller.channel.map.ChannelMap; -import controller.channel.map.ChannelMapManagerFrame; -import controller.channel.map.ChannelMapModel; - -public class MPT1327ConfigEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JComboBox mComboChannelMaps; - private JComboBox mComboSync; - private JLabel mCallTimeoutLabel; - private JSlider mCallTimeout; - private JLabel mTrafficChannelPoolSizeLabel; - private JSlider mTrafficChannelPoolSize; - private ChannelMapModel mChannelMapModel; - - public MPT1327ConfigEditor( DecodeConfiguration config, - ChannelMapModel channelMapModel ) - { - super( config ); - - mChannelMapModel = channelMapModel; - - initGUI(); - } - - private DecodeConfigMPT1327 getMPTConfig() - { - return (DecodeConfigMPT1327)mConfig; - } - - private void initGUI() - { - /** - * ComboBox: Alias Lists - */ - List maps = mChannelMapModel.getChannelMaps(); - ChannelMap[] mapArray = maps.toArray( new ChannelMap[ maps.size() ] ); - - mComboChannelMaps = new JComboBox(); - mComboChannelMaps.setModel( - new DefaultComboBoxModel( mapArray ) ); - - add( new JLabel( "Channel Map:" ) ); - add( mComboChannelMaps, "wrap" ); - - JButton channelMapManager = new JButton( "Channel Map Manager ..." ); - channelMapManager.addActionListener( new ActionListener() - { - @Override - public void actionPerformed( ActionEvent e ) - { - final ChannelMapManagerFrame manager = - new ChannelMapManagerFrame( mChannelMapModel ); - - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - manager.setVisible( true ); - } - } ); - } - } ); - add( channelMapManager, "wrap" ); - - mComboSync = new JComboBox(); - mComboSync.setModel( new DefaultComboBoxModel( Sync.values() ) ); - - add( new JLabel( "Sync:" ) ); - add( mComboSync, "wrap" ); - - mCallTimeout = new JSlider( JSlider.HORIZONTAL, - DecodeConfiguration.CALL_TIMEOUT_MINIMUM, - DecodeConfiguration.CALL_TIMEOUT_MAXIMUM, - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); - - mCallTimeout.setMajorTickSpacing( 90 ); - mCallTimeout.setMinorTickSpacing( 10 ); - mCallTimeout.setPaintTicks( true ); - - mCallTimeout.setLabelTable( mCallTimeout.createStandardLabels( 100, 100 ) ); - mCallTimeout.setPaintLabels( true ); - - mCallTimeoutLabel = new JLabel( "Call Timeout: " + mCallTimeout.getValue() + " " ); - - mCallTimeout.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mCallTimeoutLabel.setText( "Call Timeout: " + mCallTimeout.getValue() ); - } - } ); - - add( mCallTimeoutLabel ); - add( mCallTimeout, "wrap,grow" ); - - mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); - - mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); - mTrafficChannelPoolSize.setMinorTickSpacing( 1 ); - mTrafficChannelPoolSize.setPaintTicks( true ); - - mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) ); - mTrafficChannelPoolSize.setPaintLabels( true ); - - mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() + " " ); - - mTrafficChannelPoolSize.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() ); - } - } ); - - add( mTrafficChannelPoolSizeLabel ); - add( mTrafficChannelPoolSize, "wrap,grow" ); - -// reset(); - } - - @Override - public void save() - { - DecodeConfigMPT1327 config = getMPTConfig(); - - ChannelMap selected = (ChannelMap)mComboChannelMaps.getSelectedItem(); - - if( selected != null ) - { - config.setChannelMapName( selected.getName() ); - } - - Sync selectedSync = (Sync)mComboSync.getSelectedItem(); - - if( selectedSync != null ) - { - config.setSync( selectedSync ); - } - - config.setCallTimeout( mCallTimeout.getValue() ); - - config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); - } - -// @Override -// public void reset() -// { -// DecodeConfigMPT1327 config = (DecodeConfigMPT1327)mConfig; -// -// String name = config.getChannelMapName(); -// -// ChannelMap selected = null; -// -// if( name != null ) -// { -// for( ChannelMap map: mPlaylistManager.getPlayist().getChannelMapList() -// .getChannelMap() ) -// { -// if( map.getName().equalsIgnoreCase( name ) ) -// { -// selected = map; -// } -// } -// } -// -// mComboChannelMaps.setSelectedItem( selected ); -// mComboChannelMaps.requestFocus(); -// mComboChannelMaps.requestFocusInWindow(); -// -// mComboSync.setSelectedItem( config.getSync() ); -// -// mCallTimeout.setValue( config.getCallTimeout() ); -// -// mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); -// } -} diff --git a/src/module/decode/mpt1327/MPT1327DecoderEditor.java b/src/module/decode/mpt1327/MPT1327DecoderEditor.java new file mode 100644 index 00000000..fd2bc636 --- /dev/null +++ b/src/module/decode/mpt1327/MPT1327DecoderEditor.java @@ -0,0 +1,287 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode.mpt1327; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.EventQueue; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.config.DecodeConfiguration; +import module.decode.ltrnet.DecodeConfigLTRNet; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; +import controller.channel.map.ChannelMap; +import controller.channel.map.ChannelMapManagerFrame; +import controller.channel.map.ChannelMapModel; + +public class MPT1327DecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + private JComboBox mComboChannelMaps; + private JLabel mCallTimeoutLabel; + private JSlider mCallTimeout; + private JLabel mTrafficChannelPoolSizeLabel; + private JSlider mTrafficChannelPoolSize; + private ChannelMapModel mChannelMapModel; + + public MPT1327DecoderEditor( ChannelMapModel channelMapModel ) + { + mChannelMapModel = channelMapModel; + + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + + JButton channelMaps = new JButton( "Channel Maps" ); + channelMaps.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + final ChannelMapManagerFrame manager = + new ChannelMapManagerFrame( mChannelMapModel ); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + manager.setVisible( true ); + } + } ); + } + } ); + add( channelMaps, "span 2" ); + + List maps = mChannelMapModel.getChannelMaps(); + ChannelMap[] mapArray = maps.toArray( new ChannelMap[ maps.size() ] ); + + mComboChannelMaps = new JComboBox(); + mComboChannelMaps.setModel( new DefaultComboBoxModel( mapArray ) ); + mComboChannelMaps.setEnabled( false ); + add( new JLabel( "Channel Map:" ) ); + add( mComboChannelMaps, "span 3,grow" ); + + 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 ); + mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Pool: " + + mTrafficChannelPoolSize.getValue() + " " ); + + mTrafficChannelPoolSize.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mTrafficChannelPoolSizeLabel.setText( "Traffic Pool: " + + mTrafficChannelPoolSize.getValue() ); + } + } ); + + add( mTrafficChannelPoolSizeLabel ); + add( mTrafficChannelPoolSize ); + + mCallTimeout = new JSlider( JSlider.HORIZONTAL, DecodeConfiguration.CALL_TIMEOUT_MINIMUM, + DecodeConfiguration.CALL_TIMEOUT_MAXIMUM, DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); + mCallTimeout.setEnabled( false ); + mCallTimeout.setMajorTickSpacing( 100 ); + mCallTimeout.setMinorTickSpacing( 50 ); + mCallTimeout.setPaintTicks( true ); + + mCallTimeoutLabel = new JLabel( "Timeout: " + mCallTimeout.getValue() + " " ); + + mCallTimeout.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mCallTimeoutLabel.setText( "Timeout: " + mCallTimeout.getValue() ); + } + } ); + + add( mCallTimeoutLabel ); + add( mCallTimeout ); + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigMPT1327 mpt = new DecodeConfigMPT1327(); + mpt.setAFC( mAFC.isSelected() ); + mpt.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + mpt.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); + mpt.setCallTimeout( mCallTimeout.getValue() ); + + if( mComboChannelMaps.getSelectedItem() != null ) + { + ChannelMap map = (ChannelMap)mComboChannelMaps.getSelectedItem(); + + mpt.setChannelMapName( map.getName() ); + } + + getItem().setDecodeConfiguration( mpt ); + } + + setModified( false ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation required + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAFC.isEnabled() != enabled ) + { + mAFC.setEnabled( enabled ); + } + + if( mComboChannelMaps.isEnabled() != enabled ) + { + mComboChannelMaps.setEnabled( enabled ); + } + + if( mTrafficChannelPoolSize.isEnabled() != enabled ) + { + mTrafficChannelPoolSize.setEnabled( enabled ); + } + + if( mCallTimeout.isEnabled() != enabled ) + { + mCallTimeout.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigMPT1327 ) + { + DecodeConfigMPT1327 mpt = (DecodeConfigMPT1327)config; + mAFC.setSelected( mpt.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( mpt.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( mpt.isAFCEnabled() ); + + ChannelMap map = mChannelMapModel.getChannelMap( mpt.getChannelMapName() ); + + if( map != null ) + { + mComboChannelMaps.setSelectedItem( map ); + } + + mTrafficChannelPoolSize.setValue( mpt.getTrafficChannelPoolSize() ); + mCallTimeout.setValue( mpt.getCallTimeout() ); + + setModified( false ); + } + else + { + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + mTrafficChannelPoolSize.setValue( DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + mCallTimeout.setValue( DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS ); + + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/nbfm/NBFMDecoderEditor.java b/src/module/decode/nbfm/NBFMDecoderEditor.java new file mode 100644 index 00000000..9d6d025c --- /dev/null +++ b/src/module/decode/nbfm/NBFMDecoderEditor.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode.nbfm; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class NBFMDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + + public NBFMDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 2", "[right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigNBFM nbfm = new DecodeConfigNBFM(); + + nbfm.setAFC( mAFC.isSelected() ); + nbfm.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + + getItem().setDecodeConfiguration( nbfm ); + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAFC.isEnabled() != enabled ) + { + mAFC.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigNBFM ) + { + DecodeConfigNBFM nbfm = (DecodeConfigNBFM)config; + + mAFC.setSelected( nbfm.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( nbfm.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( nbfm.isAFCEnabled() ); + + setModified( false ); + } + else + { + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/nbfm/NBFMEditor.java b/src/module/decode/nbfm/NBFMEditor.java deleted file mode 100644 index d80e099a..00000000 --- a/src/module/decode/nbfm/NBFMEditor.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.nbfm; - -import javax.swing.JCheckBox; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class NBFMEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - private JCheckBox mAFC; - - public NBFMEditor( DecodeConfiguration config ) - { - super( config ); - } -} diff --git a/src/module/decode/p25/P25DecodeEditor.java b/src/module/decode/p25/P25DecodeEditor.java deleted file mode 100644 index e381cc28..00000000 --- a/src/module/decode/p25/P25DecodeEditor.java +++ /dev/null @@ -1,147 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.p25; - -import javax.swing.DefaultComboBoxModel; -import javax.swing.JCheckBox; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JSlider; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; -import module.decode.p25.P25Decoder.Modulation; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import source.SourceConfigurationEditor; -import source.tuner.TunerSourceEditor; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; - -public class P25DecodeEditor extends DecodeEditor -{ - private final static Logger mLog = LoggerFactory.getLogger( P25DecodeEditor.class ); - - private static final long serialVersionUID = 1L; - - private JComboBox mComboModulation; - private JCheckBox mIgnoreDataCalls; - private JLabel mTrafficChannelPoolSizeLabel; - private JSlider mTrafficChannelPoolSize; - - public P25DecodeEditor( DecodeConfiguration config ) - { - super( config ); - - initGUI(); - } - - private void initGUI() - { - final DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; - - mComboModulation = new JComboBox(); - - mComboModulation.setModel( new DefaultComboBoxModel( - P25_LSMDecoder.Modulation.values() ) ); - - mComboModulation.setSelectedItem( config.getModulation() ); - - add( new JLabel( "Modulation:" ) ); - add( mComboModulation, "wrap" ); - - mTrafficChannelPoolSize = new JSlider( JSlider.HORIZONTAL, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MINIMUM, - DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_MAXIMUM, - config.getTrafficChannelPoolSize() ); - - mTrafficChannelPoolSize.setMajorTickSpacing( 10 ); - mTrafficChannelPoolSize.setMinorTickSpacing( 1 ); - mTrafficChannelPoolSize.setPaintTicks( true ); - - mTrafficChannelPoolSize.setLabelTable( mTrafficChannelPoolSize.createStandardLabels( 10, 10 ) ); - mTrafficChannelPoolSize.setPaintLabels( true ); - - mTrafficChannelPoolSizeLabel = new JLabel( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() + " " ); - - mTrafficChannelPoolSize.addChangeListener( new ChangeListener() - { - @Override - public void stateChanged( ChangeEvent e ) - { - mTrafficChannelPoolSizeLabel.setText( "Traffic Channel Pool: " + mTrafficChannelPoolSize.getValue() ); - } - } ); - - add( mTrafficChannelPoolSizeLabel ); - add( mTrafficChannelPoolSize, "wrap,grow" ); - - mIgnoreDataCalls = new JCheckBox(); - mIgnoreDataCalls.setSelected( config.getIgnoreDataCalls() ); - - add( new JLabel( "Ignore Data Calls" ) ); - add( mIgnoreDataCalls, "wrap,grow" ); - -// reset(); - } - -// /** -// * Enforce source=tuner for CQPSK modulation -// */ -// @Override -// public void validate( ChannelConfigurationEditor editor ) throws ConfigurationValidationException -// { -// if( editor instanceof SourceEditor && -// ((DecodeConfigP25Phase1)mConfig).getModulation() == Modulation.CQPSK ) -// { -// if( !( editor instanceof TunerEditor ) ) -// { -// throw new ConfigurationValidationException( -// "

    LSM Simulcast

    " -// + "

    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" ); -// -// } -// } -// } - - @Override - public void save() - { - DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; - - config.setModulation( (Modulation)mComboModulation.getSelectedItem() ); - config.setIgnoreDataCalls( mIgnoreDataCalls.isSelected() ); - config.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); - } - -// @Override -// public void reset() -// { -// DecodeConfigP25Phase1 config = (DecodeConfigP25Phase1)mConfig; -// -// mComboModulation.setSelectedItem( config.getModulation() ); -// mIgnoreDataCalls.setSelected( config.getIgnoreDataCalls() ); -// mTrafficChannelPoolSize.setValue( config.getTrafficChannelPoolSize() ); -// } -} diff --git a/src/module/decode/p25/P25DecoderEditor.java b/src/module/decode/p25/P25DecoderEditor.java new file mode 100644 index 00000000..e647e407 --- /dev/null +++ b/src/module/decode/p25/P25DecoderEditor.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode.p25; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.AuxDecodeConfigurationEditor; +import module.decode.config.AuxDecodeConfiguration; +import module.decode.config.DecodeConfiguration; +import module.decode.p25.P25Decoder.Modulation; +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceConfigurationEditor; +import source.config.SourceConfigTuner; +import source.config.SourceConfiguration; +import controller.channel.Channel; + +public class P25DecoderEditor extends ValidatingEditor +{ + private final static Logger mLog = LoggerFactory.getLogger( P25DecoderEditor.class ); + + private static final long serialVersionUID = 1L; + + private JComboBox mComboModulation; + private JCheckBox mIgnoreDataCalls; + private JLabel mTrafficChannelPoolSizeLabel; + private JSlider mTrafficChannelPoolSize; + + public P25DecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 3", "[right][grow,fill][]", "" ) ); + + mComboModulation = new JComboBox(); + mComboModulation.setModel( new DefaultComboBoxModel( + P25_LSMDecoder.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( (Modulation)mComboModulation.getSelectedItem() ); + p25.setIgnoreDataCalls( mIgnoreDataCalls.isSelected() ); + p25.setTrafficChannelPoolSize( mTrafficChannelPoolSize.getValue() ); + + getItem().setDecodeConfiguration( p25 ); + } + + setModified( false ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + mLog.debug( "Validating editor:" + editor.getClass() ); + /** + * Enforce CQPSK modulation uses a Tuner Source for I/Q sample data + */ + if( editor instanceof SourceConfigurationEditor && + ((Modulation)mComboModulation.getSelectedItem()) == Modulation.CQPSK ) + { + SourceConfiguration config = ((SourceConfigurationEditor)editor).getSourceConfiguration(); + + if( config != null && !( config instanceof SourceConfigTuner ) ) + { + throw new EditorValidationException( editor, + "

    LSM Simulcast

    " + + "

    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

    " ); + } + } + + if( editor instanceof AuxDecodeConfigurationEditor ) + { + AuxDecodeConfiguration config = ((AuxDecodeConfigurationEditor)editor).getConfiguration(); + + if( config != null && !config.getAuxDecoders().isEmpty() ) + { + throw new EditorValidationException( editor, + "

    Auxilary Decoders

    " + + "

    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

    " ); + } + } + } + + 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( Modulation.C4FM ); + mIgnoreDataCalls.setSelected( false ); + mTrafficChannelPoolSize.setValue( DecodeConfiguration.TRAFFIC_CHANNEL_LIMIT_DEFAULT ); + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/passport/PassportDecoderEditor.java b/src/module/decode/passport/PassportDecoderEditor.java new file mode 100644 index 00000000..e40dd11a --- /dev/null +++ b/src/module/decode/passport/PassportDecoderEditor.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * SDR Trunk + * Copyright (C) 2014-2016 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 + ******************************************************************************/ +package module.decode.passport; + +import gui.editor.Editor; +import gui.editor.EditorValidationException; +import gui.editor.ValidatingEditor; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JCheckBox; +import javax.swing.JSlider; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import module.decode.config.DecodeConfiguration; +import net.miginfocom.swing.MigLayout; +import controller.channel.Channel; + +public class PassportDecoderEditor extends ValidatingEditor +{ + private static final long serialVersionUID = 1L; + + private JCheckBox mAFC; + private JSlider mAFCMaximumCorrection; + + public PassportDecoderEditor() + { + init(); + } + + private void init() + { + setLayout( new MigLayout( "insets 0 0 0 0,wrap 2", "[right][grow,fill]", "" ) ); + + mAFC = new JCheckBox( "AFC: 3000 Hz" ); + mAFC.setEnabled( false ); + mAFC.setToolTipText( "AFC automatically adjusts the center frequency of the channel to " + + "correct/compensate for inaccuracies and frequency drift in the tuner" ); + mAFC.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + setModified( true ); + + if( mAFC.isSelected() && !mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( true ); + } + else if( !mAFC.isSelected() && mAFCMaximumCorrection.isEnabled() ) + { + mAFCMaximumCorrection.setEnabled( false ); + } + } + } ); + + add( mAFC ); + + mAFCMaximumCorrection = new JSlider( 0, 7000, 3000 ); + mAFCMaximumCorrection.setEnabled( false ); + mAFCMaximumCorrection.setToolTipText( "Maximum AFC frequency correction (0 - 15kHz)" ); + mAFCMaximumCorrection.setMajorTickSpacing( 2000 ); + mAFCMaximumCorrection.setMinorTickSpacing( 1000 ); + mAFCMaximumCorrection.setPaintTicks( true ); + + mAFCMaximumCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + mAFC.setText( "AFC: " + mAFCMaximumCorrection.getValue() + " Hz" ); + setModified( true ); + } + } ); + add( mAFCMaximumCorrection ); + } + + @Override + public void validate( Editor editor ) throws EditorValidationException + { + //No validation + } + + @Override + public void save() + { + if( hasItem() && isModified() ) + { + DecodeConfigPassport passport = new DecodeConfigPassport(); + + passport.setAFC( mAFC.isSelected() ); + passport.setAFCMaximumCorrection( mAFCMaximumCorrection.getValue() ); + + getItem().setDecodeConfiguration( passport ); + } + + setModified( false ); + } + + private void setControlsEnabled( boolean enabled ) + { + if( mAFC.isEnabled() != enabled ) + { + mAFC.setEnabled( enabled ); + } + } + + @Override + public void setItem( Channel item ) + { + super.setItem( item ); + + if( hasItem() ) + { + setControlsEnabled( true ); + + DecodeConfiguration config = getItem().getDecodeConfiguration(); + + if( config instanceof DecodeConfigPassport ) + { + DecodeConfigPassport passport = (DecodeConfigPassport)config; + + mAFC.setSelected( passport.isAFCEnabled() ); + mAFCMaximumCorrection.setValue( passport.getAFCMaximumCorrection() ); + mAFCMaximumCorrection.setEnabled( passport.isAFCEnabled() ); + setModified( false ); + } + else + { + mAFC.setSelected( false ); + mAFCMaximumCorrection.setValue( DecodeConfiguration.DEFAULT_AFC_MAX_CORRECTION ); + mAFCMaximumCorrection.setEnabled( false ); + setModified( true ); + } + } + else + { + setControlsEnabled( false ); + setModified( false ); + } + } +} diff --git a/src/module/decode/passport/PassportEditor.java b/src/module/decode/passport/PassportEditor.java deleted file mode 100644 index 4b39e15d..00000000 --- a/src/module/decode/passport/PassportEditor.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package module.decode.passport; - -import module.decode.DecodeEditor; -import module.decode.config.DecodeConfiguration; - -public class PassportEditor extends DecodeEditor -{ - private static final long serialVersionUID = 1L; - - public PassportEditor( DecodeConfiguration config ) - { - super( config ); - } -} diff --git a/src/record/RecordEditor.java b/src/record/RecordEditor.java deleted file mode 100644 index 9913bf9e..00000000 --- a/src/record/RecordEditor.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * SDR Trunk - * Copyright (C) 2014 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 - ******************************************************************************/ -package record; - -import record.config.RecordConfiguration; -import controller.channel.Channel; -import controller.channel.ChannelConfigurationEditor; -import controller.channel.ConfigurationValidationException; - -public class RecordEditor extends ChannelConfigurationEditor -{ - private static final long serialVersionUID = 1L; - - protected RecordConfiguration mConfig; - - public RecordEditor( RecordConfiguration config ) - { - mConfig = config; - } - - public RecordConfiguration getConfig() - { - return mConfig; - } - - @Override - public void save() - { - } - - @Override - public void setConfiguration( Channel channel ) - { - // TODO Auto-generated method stub - - } - - @Override - public void validateConfiguration() throws ConfigurationValidationException - { - // TODO Auto-generated method stub - - } -} diff --git a/src/source/SourceConfigurationEditor.java b/src/source/SourceConfigurationEditor.java index e5df0ae9..2c16c299 100644 --- a/src/source/SourceConfigurationEditor.java +++ b/src/source/SourceConfigurationEditor.java @@ -25,9 +25,9 @@ import java.awt.event.ActionListener; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; -import javax.swing.JLabel; import net.miginfocom.swing.MigLayout; +import source.config.SourceConfiguration; import source.mixer.MixerSourceEditor; import source.tuner.TunerSourceEditor; import controller.channel.Channel; @@ -44,13 +44,22 @@ public class SourceConfigurationEditor extends Editor public SourceConfigurationEditor( SourceManager sourceManager ) { mMixerSourceEditor = new MixerSourceEditor( sourceManager ); + mMixerSourceEditor.setSaveRequestListener( this ); + mTunerSourceEditor = new TunerSourceEditor(); + mTunerSourceEditor.setSaveRequestListener( this ); + init(); } + + public SourceConfiguration getSourceConfiguration() + { + return hasItem() ? getItem().getSourceConfiguration() : null; + } private void init() { - setLayout( new MigLayout( "fill,wrap 3", "[right][left][grow,fill]", "[][grow]" ) ); + setLayout( new MigLayout( "wrap 2", "[][grow,fill]", "[align top][grow]" ) ); mComboSources = new JComboBox(); mComboSources.setModel( new DefaultComboBoxModel( SourceType.getTypes() ) ); @@ -76,7 +85,6 @@ public class SourceConfigurationEditor extends Editor } }); - add( new JLabel( "Source:" ) ); add( mComboSources ); mCurrentEditor = mTunerSourceEditor; @@ -87,10 +95,16 @@ public class SourceConfigurationEditor extends Editor { if( mCurrentEditor != editor ) { + //Set channel to null to force a save prompt as required + mCurrentEditor.setItem( null ); + remove( mCurrentEditor ); mCurrentEditor = editor; + mCurrentEditor.setSaveRequestListener( this ); add( mCurrentEditor ); + mCurrentEditor.setItem( getItem() ); + revalidate(); } } @@ -98,18 +112,12 @@ public class SourceConfigurationEditor extends Editor @Override public void save() { - if( hasItem() && mCurrentEditor.isModified() ) + if( hasItem() ) { mCurrentEditor.save(); } } - @Override - public boolean isModified() - { - return super.isModified() || mCurrentEditor.isModified(); - } - /** * Sets the channel configuration. Note: this method must be invoked from * the swing event dispatch thread. @@ -121,8 +129,9 @@ public class SourceConfigurationEditor extends Editor if( hasItem() ) { - mMixerSourceEditor.setItem( channel ); - mTunerSourceEditor.setItem( channel ); + mComboSources.setSelectedItem( getItem().getSourceConfiguration().getSourceType() ); + repaint(); + mCurrentEditor.setItem( channel ); } } } diff --git a/src/source/mixer/MixerSourceEditor.java b/src/source/mixer/MixerSourceEditor.java index fdae5537..11ad5516 100644 --- a/src/source/mixer/MixerSourceEditor.java +++ b/src/source/mixer/MixerSourceEditor.java @@ -50,7 +50,7 @@ public class MixerSourceEditor extends Editor private void init() { - setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][grow]" ) ); + setLayout( new MigLayout( "insets 0 0 0 0,wrap 4", "[right][grow,fill][right][grow,fill]", "" ) ); mComboMixers = new JComboBox(); InputMixerConfiguration[] inputMixers = mSourceManager.getMixerManager().getInputMixers(); @@ -68,6 +68,8 @@ public class MixerSourceEditor extends Editor mComboChannels.setModel( new DefaultComboBoxModel( channels.toArray( new MixerChannel[ channels.size() ] ) ) ); + repaint(); + setModified( true ); } } ); @@ -76,6 +78,17 @@ public class MixerSourceEditor extends Editor add( mComboMixers ); mComboChannels = new JComboBox(); + + InputMixerConfiguration selected = (InputMixerConfiguration)mComboMixers.getSelectedItem(); + + if( selected != null ) + { + EnumSet channels = selected.getChannels(); + + mComboChannels.setModel( new DefaultComboBoxModel( + channels.toArray( new MixerChannel[ channels.size() ] ) ) ); + } + mComboChannels.setEnabled( false ); mComboChannels.addActionListener( new ActionListener() { @@ -114,24 +127,33 @@ public class MixerSourceEditor extends Editor for( MixerChannel channel: inputMixer.getChannels() ) { - if( channel.getLabel().equalsIgnoreCase( mixer.getChannel().getLabel() ) ) + if( channel.getLabel() != null && mixer.getChannel() != null ) { - mComboChannels.setSelectedItem( channel ); + if( channel.getLabel().equalsIgnoreCase( mixer.getChannel().getLabel() ) ) + { + mComboChannels.setSelectedItem( channel ); + } } } continue; } } + + setModified( false ); + } + else + { + setModified( true ); } } else { mComboMixers.setEnabled( false ); mComboChannels.setEnabled( false ); + + setModified( false ); } - - setModified( false ); } public void save() diff --git a/src/source/tuner/TunerSourceEditor.java b/src/source/tuner/TunerSourceEditor.java index 1bc26ee6..4973e6b7 100644 --- a/src/source/tuner/TunerSourceEditor.java +++ b/src/source/tuner/TunerSourceEditor.java @@ -38,7 +38,7 @@ public class TunerSourceEditor extends Editor private void init() { - setLayout( new MigLayout( "fill", "[left]", "[][grow]" ) ); + setLayout( new MigLayout( "insets 0 0 0 0", "[left]", "" ) ); mFrequencyControl = new JFrequencyControl(); mFrequencyControl.setEnabled( false ); mFrequencyControl.addListener( new IFrequencyChangeProcessor() @@ -73,18 +73,23 @@ public class TunerSourceEditor extends Editor { SourceConfiguration config = getItem().getSourceConfiguration(); + mFrequencyControl.setEnabled( true ); + if( config instanceof SourceConfigTuner ) { mFrequencyControl.setFrequency( ((SourceConfigTuner)config).getFrequency(), false ); + setModified( false ); + } + else + { + mFrequencyControl.setFrequency( 101000000, false ); + setModified( true ); } - - mFrequencyControl.setEnabled( true ); } else { mFrequencyControl.setEnabled( false ); + setModified( false ); } - - setModified( false ); } } diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index 7e1aadf1..ded12c2a 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -100,8 +100,7 @@ public class OverlayPanel extends JPanel // private List mChannels = new ArrayList(); // //Currently visible/displayable channels - private CopyOnWriteArrayList mVisibleChannels = - new CopyOnWriteArrayList(); + private CopyOnWriteArrayList mVisibleChannels = new CopyOnWriteArrayList(); private ChannelDisplay mChannelDisplay = ChannelDisplay.ALL; //Defines the offset at the bottom of the spectral display to account for @@ -597,29 +596,24 @@ public class OverlayPanel extends JPanel width ); //Draw the decoder label - drawLabel( graphics, - channel.getDecodeConfiguration().getDecoderType() - .getShortDisplayString(), - this.getFont(), - xAxis, - yAxis, - width ); - } - - long frequency = tunerChannel.getFrequency(); - - double frequencyAxis = getAxisFromFrequency( frequency ); - - drawChannelCenterLine( graphics, frequencyAxis ); - - /* Draw Automatic Frequency Control line */ - int correction = channel.getChannelFrequencyCorrection(); - - if( correction != 0 ) - { - long error = frequency + correction; + drawLabel( graphics, channel.getDecodeConfiguration().getDecoderType().getShortDisplayString(), + this.getFont(), xAxis, yAxis, width ); + + long frequency = tunerChannel.getFrequency(); - drawAFC( graphics, frequencyAxis, getAxisFromFrequency( error ) ); + double frequencyAxis = getAxisFromFrequency( frequency ); + + drawChannelCenterLine( graphics, frequencyAxis ); + + /* Draw Automatic Frequency Control line */ + int correction = channel.getChannelFrequencyCorrection(); + + if( correction != 0 ) + { + long error = frequency + correction; + + drawAFC( graphics, frequencyAxis, getAxisFromFrequency( error ) ); + } } } } @@ -725,6 +719,21 @@ public class OverlayPanel extends JPanel mVisibleChannels.remove( channel ); } break; + case NOTIFICATION_CONFIGURATION_CHANGE: + if( mVisibleChannels.contains( channel ) && + !channel.isWithin( getMinFrequency(), getMaxFrequency() ) ) + { + mVisibleChannels.remove( channel ); + } + + if( !mVisibleChannels.contains( channel ) && + channel.isWithin( getMinFrequency(), getMaxFrequency() ) ) + { + mVisibleChannels.add( channel ); + } + break; + default: + break; } repaint(); From 58160d6c6baf3add189d02be837030ee06b702d9 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 26 Mar 2016 07:17:43 -0400 Subject: [PATCH 71/74] wip #34 - resolved various issues with alias controller and editors. Added synchronized method to channel processing manager to ensure that we don't have contention during channel changes. --- src/alias/AliasController.java | 54 +++++++++++++------ src/alias/AliasNameEditor.java | 2 + src/alias/MultipleAliasEditor.java | 14 +++-- .../channel/ChannelProcessingManager.java | 10 +--- src/gui/SDRTrunk.java | 6 +-- src/instrument/gui/DecoderViewFrame.java | 2 +- 6 files changed, 53 insertions(+), 35 deletions(-) diff --git a/src/alias/AliasController.java b/src/alias/AliasController.java index e6c86812..846301a4 100644 --- a/src/alias/AliasController.java +++ b/src/alias/AliasController.java @@ -9,6 +9,7 @@ import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import javax.swing.ImageIcon; @@ -177,8 +178,14 @@ public class AliasController extends JPanel if( !event.getValueIsAdjusting() ) { int[] selectedRows = mAliasTable.getSelectedRows(); - - if( selectedRows.length <= 1 ) + + if( selectedRows.length == 0 ) + { + mAliasEditor.setItem( null ); + mCopyButton.setEnabled( false ); + mDeleteButton.setEnabled( false ); + } + else if( selectedRows.length == 1 ) { setEditor( mAliasEditor ); @@ -244,17 +251,26 @@ public class AliasController extends JPanel } } - private Alias getSelectedAlias() + private List getSelectedAliases() { - int index = mAliasTable.getSelectedRow(); + List selected = new ArrayList<>(); - if( index >= 0 ) + int[] rows = mAliasTable.getSelectedRows(); + + for( int row: rows ) { - return mAliasModel.getAliasAtIndex( - mAliasTable.convertRowIndexToModel( index ) ); + if( row >= 0 ) + { + Alias alias = mAliasModel.getAliasAtIndex( mAliasTable.convertRowIndexToModel( row ) ); + + if( alias != null ) + { + selected.add( alias ); + } + } } - return null; + return selected; } /** @@ -269,25 +285,29 @@ public class AliasController extends JPanel addAlias( new Alias( "New Alias" ) ); break; case COPY_ALIAS: - Alias selected = getSelectedAlias(); - - if( selected != null ) + for( Alias alias: getSelectedAliases() ) { - addAlias( AliasFactory.copyOf( selected ) ); + addAlias( AliasFactory.copyOf( alias ) ); } break; case DELETE_ALIAS: - Alias toDelete = getSelectedAlias(); + List toDelete = getSelectedAliases(); - if( toDelete != null ) + if( toDelete != null && !toDelete.isEmpty() ) { + String title = toDelete.size() == 1 ? "Delete Alias?" : "Delete Aliases?"; + String prompt = toDelete.size() == 1 ? "Do you want to delete this alias?" : + "Do you want to delete these " + toDelete.size() + " aliases?"; + int choice = JOptionPane.showConfirmDialog( AliasController.this, - "Do you want to delete this alias?", "Delete Alias?", - JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); + prompt, title, JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE ); if( choice == JOptionPane.YES_OPTION ) { - mAliasModel.removeAlias( toDelete ); + for( Alias alias: toDelete ) + { + mAliasModel.removeAlias( alias ); + } } } break; diff --git a/src/alias/AliasNameEditor.java b/src/alias/AliasNameEditor.java index 10124091..b44996f4 100644 --- a/src/alias/AliasNameEditor.java +++ b/src/alias/AliasNameEditor.java @@ -255,6 +255,8 @@ public class AliasNameEditor extends Editor mButtonColor.setForeground( getForeground() ); } + repaint(); + setModified( false ); } diff --git a/src/alias/MultipleAliasEditor.java b/src/alias/MultipleAliasEditor.java index ae4eeeec..3e45227e 100644 --- a/src/alias/MultipleAliasEditor.java +++ b/src/alias/MultipleAliasEditor.java @@ -261,21 +261,25 @@ public class MultipleAliasEditor extends Editor> groupNames.add( 0, "" ); mGroupCombo.setModel( new DefaultComboBoxModel( groupNames.toArray( new String[ groupNames.size() ] ) ) ); + + setCheckBoxesUnselected(); setModified( false ); } - - @Override - public void reset() + + private void setCheckBoxesUnselected() { - //Reset multi-editor controls mListCheckBox.setSelected( false ); mGroupCheckBox.setSelected( false ); mColorCheckBox.setSelected( false ); mIconCheckBox.setSelected( false ); mRecordCheckBox.setSelected( false ); mPriorityCheckBox.setSelected( false ); - + } + + @Override + public void reset() + { List listNames = mAliasModel.getListNames(); listNames.add( 0, "" ); mListCombo.setModel( new DefaultComboBoxModel( diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index 52ab21a2..ee4c3766 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see ******************************************************************************/ - package controller.channel; import java.util.HashMap; @@ -33,7 +32,6 @@ import module.log.EventLogManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import playlist.PlaylistManager; import record.RecorderManager; import record.RecorderType; import sample.Listener; @@ -44,7 +42,6 @@ import alias.AliasModel; import audio.AudioPacket; import audio.metadata.Metadata; import audio.metadata.MetadataType; -import controller.ThreadPoolManager; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent.Event; import controller.channel.map.ChannelMapModel; @@ -65,15 +62,13 @@ public class ChannelProcessingManager implements ChannelEventListener private EventLogManager mEventLogManager; private RecorderManager mRecorderManager; private SourceManager mSourceManager; - private ThreadPoolManager mThreadPoolManager; public ChannelProcessingManager( ChannelModel channelModel, ChannelMapModel channelMapModel, AliasModel aliasModel, EventLogManager eventLogManager, RecorderManager recorderManager, - SourceManager sourceManager, - ThreadPoolManager threadPoolManager ) + SourceManager sourceManager ) { mChannelModel = channelModel; mChannelMapModel = channelMapModel; @@ -81,7 +76,6 @@ public class ChannelProcessingManager implements ChannelEventListener mEventLogManager = eventLogManager; mRecorderManager = recorderManager; mSourceManager = sourceManager; - mThreadPoolManager = threadPoolManager; } /** @@ -108,7 +102,7 @@ public class ChannelProcessingManager implements ChannelEventListener } @Override - public void channelChanged( ChannelEvent event ) + public synchronized void channelChanged( ChannelEvent event ) { Channel channel = event.getChannel(); diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index 83f9f7ee..40bfb51a 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -136,10 +136,8 @@ public class SDRTrunk implements Listener SourceManager sourceManager = new SourceManager( tunerModel, mSettingsManager, threadPoolManager ); - ChannelProcessingManager channelProcessingManager = - new ChannelProcessingManager( channelModel, channelMapModel, - aliasModel, eventLogManager, recorderManager, - sourceManager, threadPoolManager ); + ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager( + channelModel, channelMapModel, aliasModel, eventLogManager, recorderManager, sourceManager ); channelProcessingManager.addAudioPacketListener( recorderManager ); channelModel.addListener( channelProcessingManager ); diff --git a/src/instrument/gui/DecoderViewFrame.java b/src/instrument/gui/DecoderViewFrame.java index 09ea4383..87bfab41 100644 --- a/src/instrument/gui/DecoderViewFrame.java +++ b/src/instrument/gui/DecoderViewFrame.java @@ -104,7 +104,7 @@ public class DecoderViewFrame extends JInternalFrame ChannelProcessingManager channelProcessingManager = new ChannelProcessingManager( channelModel, channelMapModel, - new AliasModel(), new EventLogManager(), null, null, new ThreadPoolManager() ); + new AliasModel(), new EventLogManager(), null, null ); List modules = DecoderFactory.getModules( channelModel, channelMapModel, channelProcessingManager, new AliasModel(), channel ); From 3caadf0c90677dc35b4ffe395ec62dcf30d3f0f6 Mon Sep 17 00:00:00 2001 From: denny Date: Sat, 26 Mar 2016 07:29:10 -0400 Subject: [PATCH 72/74] Moves audio panel into the channel state list panel so that we don't have to use a split pane. --- src/audio/AudioChannelPanel.java | 3 --- src/audio/AudioChannelsPanel.java | 20 +++++++++++++++++--- src/controller/ControllerPanel.java | 16 ++++++++-------- src/module/decode/state/ChannelList.java | 15 ++++++++++++--- 4 files changed, 37 insertions(+), 17 deletions(-) diff --git a/src/audio/AudioChannelPanel.java b/src/audio/AudioChannelPanel.java index 298e6b18..6de7a221 100644 --- a/src/audio/AudioChannelPanel.java +++ b/src/audio/AudioChannelPanel.java @@ -124,9 +124,6 @@ public class AudioChannelPanel extends JPanel mFromAlias.setForeground( mLabelColor ); add( mFromAlias,"wrap" ); - JSeparator separator = new JSeparator( JSeparator.HORIZONTAL ); - separator.setBackground( Color.DARK_GRAY ); - add( separator, "span,growx" ); } @Override diff --git a/src/audio/AudioChannelsPanel.java b/src/audio/AudioChannelsPanel.java index 07c7604c..4043d4de 100644 --- a/src/audio/AudioChannelsPanel.java +++ b/src/audio/AudioChannelsPanel.java @@ -4,6 +4,7 @@ import java.awt.Color; import java.util.List; import javax.swing.JPanel; +import javax.swing.JSeparator; import net.miginfocom.swing.MigLayout; import settings.SettingsManager; @@ -21,17 +22,30 @@ public class AudioChannelsPanel extends JPanel setBackground( Color.BLACK ); List outputs = controller.getAudioOutputs(); - - for( AudioOutput audioOutput: controller.getAudioOutputs() ) + + for( int x = 0; x < outputs.size(); x++ ) { - add( new AudioChannelPanel( settingsManager, audioOutput ), "wrap" ); + add( new AudioChannelPanel( settingsManager, outputs.get( x ) ), "wrap" ); + + if( x < outputs.size() - 1 ) + { + addSeparator(); + } } /* Add an empty channel panel so that the panel is sized appropriately * for either a single channel or two channels */ if( outputs.size() == 1 ) { + addSeparator(); add( new AudioChannelPanel( settingsManager, null ), "wrap" ); } } + + private void addSeparator() + { + JSeparator separator = new JSeparator( JSeparator.HORIZONTAL ); + separator.setBackground( Color.DARK_GRAY ); + add( separator, "span,growx" ); + } } diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index a90becf8..1243b164 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -93,7 +93,7 @@ public class ControllerPanel extends JPanel channelProcessingManager ); mChannelStateList = new ChannelList( channelModel, channelProcessingManager, - settingsManager ); + settingsManager, mAudioPanel ); mChannelController = new ChannelController( channelModel, channelMapModel, sourceManager, aliasModel ); @@ -156,20 +156,20 @@ public class ControllerPanel extends JPanel JScrollPane channelStateListScroll = new JScrollPane(); channelStateListScroll.getViewport().setView( mChannelStateList ); - channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) ); + channelStateListScroll.setPreferredSize( new Dimension( 400, 500 ) ); - JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); - audioChannelListSplit.setDividerSize( 5 ); - audioChannelListSplit.add( mAudioPanel ); - audioChannelListSplit.add( channelStateListScroll ); +// JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); +// audioChannelListSplit.setDividerSize( 5 ); +// audioChannelListSplit.add( mAudioPanel ); +// audioChannelListSplit.add( channelStateListScroll ); //Set preferred size to influence the split between these panels - audioChannelListSplit.setPreferredSize( new Dimension( 400, 500 ) ); +// audioChannelListSplit.setPreferredSize( new Dimension( 400, 500 ) ); mTabbedPane.setPreferredSize( new Dimension( 880, 500 ) ); JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); channelSplit.setDividerSize( 5 ); - channelSplit.add( audioChannelListSplit ); + channelSplit.add( channelStateListScroll ); channelSplit.add( mTabbedPane ); add( channelSplit ); diff --git a/src/module/decode/state/ChannelList.java b/src/module/decode/state/ChannelList.java index 3c781462..9656c06f 100644 --- a/src/module/decode/state/ChannelList.java +++ b/src/module/decode/state/ChannelList.java @@ -34,6 +34,7 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import audio.AudioPanel; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; import settings.ColorSettingResetMenuItem; @@ -60,24 +61,32 @@ public class ChannelList extends JPanel implements ChannelEventListener private ChannelModel mChannelModel; private ChannelProcessingManager mChannelProcessingManager; private SettingsManager mSettingsManager; + private AudioPanel mAudioPanel; public ChannelList( ChannelModel channelModel, ChannelProcessingManager channelProcessingManager, - SettingsManager settingsManager ) + SettingsManager settingsManager, + AudioPanel audioPanel ) { mChannelModel = channelModel; mChannelProcessingManager = channelProcessingManager; mSettingsManager = settingsManager; + mAudioPanel = audioPanel; init(); } private void init() { - setLayout( new MigLayout( "insets 0 0 0 0", "[grow,fill]", "[]0[]") ); + setLayout( new MigLayout( "insets 0 0 0 0,wrap 1", "[grow,fill]", "[]0[]") ); setBackground( Color.BLACK ); addMouseListener( new ListSelectionListener() ); + + add( mAudioPanel ); + + JSeparator separator = new JSeparator( JSeparator.HORIZONTAL ); + add( separator ); } @Override @@ -103,7 +112,7 @@ public class ChannelList extends JPanel implements ChannelEventListener ChannelPanel channelPanel = new ChannelPanel( mChannelModel, mChannelProcessingManager, mSettingsManager, channel ); - add( channelPanel, "wrap" ); + add( channelPanel ); mDisplayedPanels.put( channel, channelPanel ); From 55443b42f3423a0afdca688b4ca6649e9a67ecaf Mon Sep 17 00:00:00 2001 From: denny Date: Sun, 27 Mar 2016 07:25:17 -0400 Subject: [PATCH 73/74] wip #34 - resolved issue with airspy correctly loading stored tuner configuration values. Updated remaining tuner editors to set loading flag when loading tuner configuration and turn off after loading is complete. --- .../tuner/airspy/AirspyTunerEditor.java | 53 +++++++++++++++++-- .../tuner/fcd/proV1/FCD1TunerEditor.java | 10 ++-- .../tuner/fcd/proplusV2/FCD2TunerEditor.java | 9 ++-- .../tuner/hackrf/HackRFTunerEditor.java | 10 ++-- src/source/tuner/rtl/e4k/E4KTunerEditor.java | 10 ++-- .../tuner/rtl/r820t/R820TTunerEditor.java | 10 ++-- 6 files changed, 75 insertions(+), 27 deletions(-) diff --git a/src/source/tuner/airspy/AirspyTunerEditor.java b/src/source/tuner/airspy/AirspyTunerEditor.java index ee034ca3..10e1e567 100644 --- a/src/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/source/tuner/airspy/AirspyTunerEditor.java @@ -432,6 +432,8 @@ public class AirspyTunerEditor extends TunerConfigurationEditor try { mController.setMixerAGC( mMixerAGC.isSelected() ); + mMixerGain.setEnabled( !mMixerAGC.isSelected() ); + save(); } catch ( Exception e1 ) @@ -452,6 +454,8 @@ public class AirspyTunerEditor extends TunerConfigurationEditor try { mController.setLNAAGC( mLNAAGC.isSelected() ); + mLNAGain.setEnabled( !mLNAAGC.isSelected() ); + save(); } catch ( Exception e1 ) @@ -563,15 +567,16 @@ public class AirspyTunerEditor extends TunerConfigurationEditor double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); config.setFrequencyCorrection( value ); - config.setGain( Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ) ); + Gain gain = Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ); + + config.setGain( gain ); config.setIFGain( mIFGain.getValue() ); config.setMixerGain( mMixerGain.getValue() ); config.setLNAGain( mLNAGain.getValue() ); config.setMixerAGC( mMixerAGC.isSelected() ); config.setLNAAGC( mLNAAGC.isSelected() ); - getTunerConfigurationModel().broadcast( - new TunerConfigurationEvent( getConfiguration(), Event.CHANGE ) ); + getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); } } @@ -665,6 +670,8 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { super.setItem( config ); + mLoading = true; + if( hasItem() && getItem().isAssigned() ) { AirspyTunerConfiguration airspy = getConfiguration(); @@ -686,12 +693,52 @@ public class AirspyTunerEditor extends TunerConfigurationEditor //This will set the master, IF, Mixer, LNA and AGC controls mGainModeCombo.setSelectedItem( mode ); + + if( mode == GainMode.CUSTOM ) + { + mMasterGain.setValue( airspy.getGain().getValue() ); + mIFGain.setValue( airspy.getIFGain() ); + + if( airspy.isMixerAGC() ) + { + mMixerAGC.setSelected( airspy.isMixerAGC() ); + mMixerGain.setEnabled( false ); + } + else + { + mMixerGain.setValue( airspy.getMixerGain() ); + } + + if( airspy.isLNAAGC() ) + { + mLNAAGC.setSelected( airspy.isLNAAGC() ); + mLNAGain.setEnabled( false ); + } + else + { + mLNAGain.setValue( airspy.getLNAGain() ); + } + } + else + { + mMixerAGC.setSelected( false ); + mLNAAGC.setSelected( false ); + + Gain gain = airspy.getGain(); + + mMasterGain.setValue( gain.getValue() ); + mIFGain.setValue( gain.getIF() ); + mMixerGain.setValue( gain.getMixer() ); + mLNAGain.setValue( gain.getLNA() ); + } } else { setControlsEnabled( false ); mConfigurationName.setText( hasItem() ? getItem().getName() : "" ); } + + mLoading = false; } private String getTunerInfo() diff --git a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java index bd8ef493..5c8f834b 100644 --- a/src/source/tuner/fcd/proV1/FCD1TunerEditor.java +++ b/src/source/tuner/fcd/proV1/FCD1TunerEditor.java @@ -425,13 +425,13 @@ public class FCD1TunerEditor extends TunerConfigurationEditor { super.setItem( tunerConfiguration ); + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + if( hasItem() ) { FCD1TunerConfiguration config = getConfiguration(); - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); @@ -450,14 +450,14 @@ public class FCD1TunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( config.getName() ); } - - mLoading = false; } else { setControlsEnabled( false ); mConfigurationName.setText( "" ); } + + mLoading = false; } public enum Correction { GAIN, PHASE, DC_INPHASE, DC_QUADRATURE }; diff --git a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java index d358c46f..1e0ab570 100644 --- a/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java +++ b/src/source/tuner/fcd/proplusV2/FCD2TunerEditor.java @@ -295,12 +295,13 @@ public class FCD2TunerEditor extends TunerConfigurationEditor { super.setItem( tunerConfiguration ); + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + if( hasItem() ) { FCD2TunerConfiguration config = getConfiguration(); - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; if( tunerConfiguration.isAssigned() ) { @@ -315,13 +316,13 @@ public class FCD2TunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( config.getName() ); } - - mLoading = false; } else { setControlsEnabled( false ); mConfigurationName.setText( "" ); } + + mLoading = false; } } diff --git a/src/source/tuner/hackrf/HackRFTunerEditor.java b/src/source/tuner/hackrf/HackRFTunerEditor.java index 81542ef5..1b0095d1 100644 --- a/src/source/tuner/hackrf/HackRFTunerEditor.java +++ b/src/source/tuner/hackrf/HackRFTunerEditor.java @@ -407,13 +407,13 @@ public class HackRFTunerEditor extends TunerConfigurationEditor { super.setItem( tunerConfiguration ); + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + if( hasItem() ) { HackRFTunerConfiguration config = getConfiguration(); - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); @@ -430,14 +430,14 @@ public class HackRFTunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( config.getName() ); } - - mLoading = false; } else { setControlsEnabled( false ); mConfigurationName.setText( "" ); } + + mLoading = false; } @Override diff --git a/src/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/source/tuner/rtl/e4k/E4KTunerEditor.java index 1ea51265..5989fdf2 100644 --- a/src/source/tuner/rtl/e4k/E4KTunerEditor.java +++ b/src/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -463,13 +463,13 @@ public class E4KTunerEditor extends TunerConfigurationEditor { super.setItem( tunerConfiguration ); + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + if( hasItem() ) { E4KTunerConfiguration config = getConfiguration(); - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); @@ -487,13 +487,13 @@ public class E4KTunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( config.getName() ); } - - mLoading = false; } else { setControlsEnabled( false ); mConfigurationName.setText( "" ); } + + mLoading = false; } } diff --git a/src/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/source/tuner/rtl/r820t/R820TTunerEditor.java index 7c325eb0..0d55490c 100644 --- a/src/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -524,13 +524,13 @@ public class R820TTunerEditor extends TunerConfigurationEditor { super.setItem( tunerConfiguration ); + //Toggle loading so that we don't fire a change event and schedule a settings file save + mLoading = true; + if( hasItem() ) { R820TTunerConfiguration config = getConfiguration(); - //Toggle loading so that we don't fire a change event and schedule a settings file save - mLoading = true; - if( tunerConfiguration.isAssigned() ) { setControlsEnabled( true ); @@ -547,13 +547,13 @@ public class R820TTunerEditor extends TunerConfigurationEditor setControlsEnabled( false ); mConfigurationName.setText( config.getName() ); } - - mLoading = false; } else { setControlsEnabled( false ); mConfigurationName.setText( "" ); } + + mLoading = false; } } From 64fee2c71871249ae88e56cab4a14b2e6ba7aca2 Mon Sep 17 00:00:00 2001 From: denny Date: Tue, 29 Mar 2016 20:00:23 -0400 Subject: [PATCH 74/74] wip #34 - testing and validating changes and testing decoding. --- src/controller/channel/ChannelEditor.java | 2 + .../channel/ChannelProcessingManager.java | 22 ++- .../channel/TrafficChannelEvent.java | 34 +++++ src/module/ProcessingChain.java | 22 ++- src/module/decode/p25/P25DecoderState.java | 8 +- src/module/decode/state/ChannelState.java | 125 ++++++++---------- src/module/decode/state/State.java | 7 +- .../decode/state/TrafficChannelManager.java | 42 ++---- .../state/TrafficChannelStatusListener.java | 21 --- .../audio/DemodulatedAudioFilterModule.java | 5 +- .../demodulate/fm/FMDemodulatorModule.java | 2 + src/settings/SettingsManager.java | 4 +- src/source/tuner/TunerChannelSource.java | 8 +- 13 files changed, 153 insertions(+), 149 deletions(-) create mode 100644 src/controller/channel/TrafficChannelEvent.java delete mode 100644 src/module/decode/state/TrafficChannelStatusListener.java diff --git a/src/controller/channel/ChannelEditor.java b/src/controller/channel/ChannelEditor.java index 42509675..0b787479 100644 --- a/src/controller/channel/ChannelEditor.java +++ b/src/controller/channel/ChannelEditor.java @@ -173,6 +173,8 @@ public class ChannelEditor extends Editor implements ActionListener, Ch { if( hasItem() ) { + save(); + mChannelEnableRequested = true; mChannelModel.broadcast( new ChannelEvent( getItem(), Event.REQUEST_ENABLE ) ); } diff --git a/src/controller/channel/ChannelProcessingManager.java b/src/controller/channel/ChannelProcessingManager.java index ee4c3766..91ca81f2 100644 --- a/src/controller/channel/ChannelProcessingManager.java +++ b/src/controller/channel/ChannelProcessingManager.java @@ -112,7 +112,7 @@ public class ChannelProcessingManager implements ChannelEventListener if( !mProcessingChains.containsKey( channel.getChannelID() ) || !mProcessingChains.get( channel.getChannelID() ).isProcessing() ) { - startProcessing( channel ); + startProcessing( event ); } break; case REQUEST_DISABLE: @@ -141,7 +141,7 @@ public class ChannelProcessingManager implements ChannelEventListener if( isProcessing( channel ) ) { stopProcessing( channel, false ); - startProcessing( channel ); + startProcessing( event ); } break; default: @@ -149,8 +149,10 @@ public class ChannelProcessingManager implements ChannelEventListener } } - private void startProcessing( Channel channel ) + private void startProcessing( ChannelEvent event ) { + Channel channel = event.getChannel(); + ProcessingChain processingChain = mProcessingChains.get( channel.getChannelID() ); //If we're already processing, ignore the request @@ -256,8 +258,17 @@ public class ChannelProcessingManager implements ChannelEventListener processingChain.setSource( source ); - processingChain.start(); + if( event instanceof TrafficChannelEvent ) + { + TrafficChannelEvent trafficChannelEvent = (TrafficChannelEvent)event; + + processingChain.getChannelState().configureAsTrafficChannel( + trafficChannelEvent.getTrafficChannelManager(), + trafficChannelEvent.getCallEvent() ); + } + processingChain.start(); + channel.setEnabled( true ); mProcessingChains.put( channel.getChannelID(), processingChain ); @@ -276,8 +287,7 @@ public class ChannelProcessingManager implements ChannelEventListener chain.stop(); - mChannelModel.broadcast( new ChannelEvent( channel, - Event.NOTIFICATION_PROCESSING_STOP ) ); + mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_PROCESSING_STOP ) ); if( remove ) { diff --git a/src/controller/channel/TrafficChannelEvent.java b/src/controller/channel/TrafficChannelEvent.java new file mode 100644 index 00000000..20c4aade --- /dev/null +++ b/src/controller/channel/TrafficChannelEvent.java @@ -0,0 +1,34 @@ +package controller.channel; + +import module.decode.event.CallEvent; +import module.decode.state.TrafficChannelManager; + +public class TrafficChannelEvent extends ChannelEvent +{ + private TrafficChannelManager mTrafficChannelManager; + private CallEvent mCallEvent; + + /** + * Call event with traffic channel manager for call back, and original creation event. + */ + public TrafficChannelEvent( TrafficChannelManager trafficChannelManager, + Channel channel, + Event event, + CallEvent callEvent ) + { + super( channel, event ); + + mTrafficChannelManager = trafficChannelManager; + mCallEvent = callEvent; + } + + public TrafficChannelManager getTrafficChannelManager() + { + return mTrafficChannelManager; + } + + public CallEvent getCallEvent() + { + return mCallEvent; + } +} diff --git a/src/module/ProcessingChain.java b/src/module/ProcessingChain.java index 271d1216..3556b897 100644 --- a/src/module/ProcessingChain.java +++ b/src/module/ProcessingChain.java @@ -69,7 +69,6 @@ import audio.squelch.ISquelchStateListener; import audio.squelch.ISquelchStateProvider; import audio.squelch.SquelchState; import controller.NamingThreadFactory; -import controller.ThreadPoolManager; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.IChannelEventListener; @@ -95,8 +94,7 @@ import controller.channel.IChannelEventProvider; */ public class ProcessingChain implements IChannelEventListener { - private final static Logger mLog = - LoggerFactory.getLogger( ProcessingChain.class ); + private final static Logger mLog = LoggerFactory.getLogger( ProcessingChain.class ); private Broadcaster mAudioPacketBroadcaster = new Broadcaster<>(); private Broadcaster mMetadataBroadcaster = new Broadcaster<>(); @@ -177,6 +175,11 @@ public class ProcessingChain implements IChannelEventListener mMessageBroadcaster.dispose(); mFilteredRealBufferBroadcaster.dispose(); mSquelchStateBroadcaster.dispose(); + + if( mScheduledExecutorService != null ) + { + mScheduledExecutorService.shutdownNow(); + } } /** @@ -356,7 +359,7 @@ public class ProcessingChain implements IChannelEventListener if( module instanceof IUnFilteredRealBufferListener ) { - mFilteredRealBufferBroadcaster.addListener( + mUnFilteredRealBufferBroadcaster.addListener( ((IUnFilteredRealBufferListener)module).getUnFilteredRealBufferListener() ); } } @@ -478,8 +481,11 @@ public class ProcessingChain implements IChannelEventListener } } - mScheduledExecutorService = Executors.newScheduledThreadPool( - 1, new NamingThreadFactory( "channel " + mName ) ); + if( mScheduledExecutorService == null ) + { + mScheduledExecutorService = Executors.newScheduledThreadPool( + 1, new NamingThreadFactory( "channel " + mName ) ); + } /* Start each of the modules */ for( Module module: mModules ) @@ -493,6 +499,8 @@ public class ProcessingChain implements IChannelEventListener mLog.error( "Error starting module", e ); } } + + mSource.start( mScheduledExecutorService ); } else { @@ -511,6 +519,8 @@ public class ProcessingChain implements IChannelEventListener { if( mSource != null ) { + mSource.stop(); + switch( mSource.getSampleType() ) { case COMPLEX: diff --git a/src/module/decode/p25/P25DecoderState.java b/src/module/decode/p25/P25DecoderState.java index 5fce8640..3772c0ad 100644 --- a/src/module/decode/p25/P25DecoderState.java +++ b/src/module/decode/p25/P25DecoderState.java @@ -121,6 +121,7 @@ import module.decode.p25.reference.LinkControlOpcode; import module.decode.p25.reference.Response; import module.decode.p25.reference.Service; import module.decode.p25.reference.Vendor; +import module.decode.state.ChangeChannelTimeoutEvent; import module.decode.state.ChangedAttribute; import module.decode.state.DecoderState; import module.decode.state.DecoderStateEvent; @@ -207,6 +208,10 @@ public class P25DecoderState extends DecoderState @Override public void start( ScheduledExecutorService executor ) { + if( mChannelType == ChannelType.TRAFFIC ) + { + broadcast( new ChangeChannelTimeoutEvent( this, ChannelType.TRAFFIC, 1000 ) ); + } } @Override @@ -2819,8 +2824,7 @@ public class P25DecoderState extends DecoderState broadcast( event ); } - broadcast( new TrafficChannelAllocationEvent( this, - mChannelCallMap.get( channel ) ) ); + broadcast( new TrafficChannelAllocationEvent( this, mChannelCallMap.get( channel ) ) ); if( gvcgu.hasChannelNumber2() ) { diff --git a/src/module/decode/state/ChannelState.java b/src/module/decode/state/ChannelState.java index 42dd2412..d77a62f6 100644 --- a/src/module/decode/state/ChannelState.java +++ b/src/module/decode/state/ChannelState.java @@ -21,30 +21,27 @@ import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; + +import module.Module; +import module.decode.config.DecodeConfiguration; +import module.decode.event.CallEvent; +import module.decode.event.ICallEventProvider; +import module.decode.state.DecoderStateEvent.Event; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import sample.Listener; import audio.metadata.IMetadataProvider; import audio.metadata.Metadata; import audio.metadata.MetadataReset; import audio.metadata.MetadataType; import audio.squelch.ISquelchStateProvider; import audio.squelch.SquelchState; -import controller.ThreadPoolManager; -import controller.ThreadPoolManager.ThreadType; import controller.channel.Channel.ChannelType; -import module.Module; -import module.decode.config.DecodeConfiguration; -import module.decode.event.CallEvent; -import module.decode.event.ICallEventProvider; -import module.decode.state.DecoderStateEvent.Event; -import sample.Listener; -public class ChannelState extends Module implements ICallEventProvider, - IChangedAttributeProvider, IDecoderStateEventListener, - IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider +public class ChannelState extends Module implements ICallEventProvider, IChangedAttributeProvider, + IDecoderStateEventListener, IDecoderStateEventProvider, IMetadataProvider, ISquelchStateProvider { private final static Logger mLog = LoggerFactory.getLogger( ChannelState.class ); @@ -66,18 +63,16 @@ public class ChannelState extends Module implements ICallEventProvider, private boolean mSelected = false; private long mStandardChannelFadeTimeout = FADE_TIMEOUT_DELAY; - private long mTrafficChannelFadeTimeout = - DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; + private long mTrafficChannelFadeTimeout = DecodeConfiguration.DEFAULT_CALL_TIMEOUT_SECONDS * 1000; private long mFadeTimeout; private long mEndTimeout; private StateMonitor mStateMonitor = new StateMonitor(); - private AtomicBoolean mMonitoring = new AtomicBoolean(); private ScheduledFuture mStateMonitorFuture; private ChannelType mChannelType; - private TrafficChannelStatusListener mTrafficChannelStatusListener; + private TrafficChannelManager mTrafficChannelEndListener; private CallEvent mTrafficChannelCallEvent; /** @@ -127,21 +122,20 @@ public class ChannelState extends Module implements ICallEventProvider, @Override public void start( ScheduledExecutorService executor ) { - //Start a monitoring task to check for FADE and END event timeouts - if( mMonitoring.compareAndSet( false, true ) ) + if( mStateMonitorFuture == null && mStateMonitor != null ) { - if( mStateMonitorFuture == null && mStateMonitor != null ) + if( mTrafficChannelEndListener != null ) { - try - { - mStateMonitorFuture = executor.scheduleAtFixedRate( - mStateMonitor, 0, 20, TimeUnit.MILLISECONDS ); - } - catch( RejectedExecutionException ree ) - { - mLog.error( "state monitor scheduled task rejected", ree ); - mMonitoring.set( false ); - } + setState( State.CALL ); + } + + try + { + mStateMonitorFuture = executor.scheduleAtFixedRate( mStateMonitor, 0, 20, TimeUnit.MILLISECONDS ); + } + catch( RejectedExecutionException ree ) + { + mLog.error( "state monitor scheduled task rejected", ree ); } } } @@ -149,23 +143,29 @@ public class ChannelState extends Module implements ICallEventProvider, @Override public void stop() { - mSquelchLocked = false; - - //Shutdown the state montoring task - if( mMonitoring.compareAndSet( true, false ) ) + if( mStateMonitorFuture != null ) { - if( mStateMonitorFuture != null ) - { - boolean success = mStateMonitorFuture.cancel( true ); - - if( !success ) - { - mLog.error("Couldn't stop monitoring scheduled future" ); - } - } + boolean success = mStateMonitorFuture.cancel( true ); - mStateMonitorFuture = null; + if( !success ) + { + mLog.error("Couldn't stop monitoring scheduled future" ); + } } + + mTrafficChannelEndListener = null; + + if( mTrafficChannelCallEvent != null ) + { + mTrafficChannelCallEvent.end(); + broadcast( mTrafficChannelCallEvent ); + } + + mTrafficChannelCallEvent = null; + + mStateMonitorFuture = null; + + mSquelchLocked = false; } public void dispose() @@ -203,7 +203,7 @@ public class ChannelState extends Module implements ICallEventProvider, if( mChannelType == ChannelType.TRAFFIC ) { - mFadeTimeout = mTrafficChannelFadeTimeout; + mFadeTimeout = System.currentTimeMillis() + mTrafficChannelFadeTimeout; } } @@ -235,6 +235,7 @@ public class ChannelState extends Module implements ICallEventProvider, { mFadeTimeout = System.currentTimeMillis() + mStandardChannelFadeTimeout; } + } /** @@ -387,23 +388,9 @@ public class ChannelState extends Module implements ICallEventProvider, broadcast( ChangedAttribute.CHANNEL_STATE ); - if( mTrafficChannelCallEvent != null ) + if( mTrafficChannelEndListener != null ) { - mTrafficChannelCallEvent.end(); - - broadcast( mTrafficChannelCallEvent ); - mTrafficChannelCallEvent = null; - } - - if( mTrafficChannelStatusListener != null ) - { - mTrafficChannelStatusListener.callEnd(); - mTrafficChannelStatusListener = null; - } - else - { - mLog.error( "Traffic channel status listener was null - couldn't " - + "teardown the channel"); + mTrafficChannelEndListener.callEnd( mTrafficChannelCallEvent.getChannel() ); } } @@ -528,11 +515,11 @@ public class ChannelState extends Module implements ICallEventProvider, * Registers a listener to be notified when a traffic channel call event is * completed, so that the listener can perform call tear-down */ - public void configureAsTrafficChannel( TrafficChannelStatusListener listener, - TrafficChannelAllocationEvent allocationEvent ) + public void configureAsTrafficChannel( TrafficChannelManager manager, CallEvent callEvent ) { - mTrafficChannelStatusListener = listener; - mTrafficChannelCallEvent = allocationEvent.getCallEvent(); + mTrafficChannelEndListener = manager; + + mTrafficChannelCallEvent = callEvent; /* Broadcast the call event details as metadata for the audio manager */ String channel = mTrafficChannelCallEvent.getChannel(); @@ -541,9 +528,9 @@ public class ChannelState extends Module implements ICallEventProvider, { broadcast( new Metadata( MetadataType.CHANNEL_NUMBER, channel, true ) ); } - + broadcast( new Metadata( MetadataType.PROTOCOL, - mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) ); + mTrafficChannelCallEvent.getDecoderType().getDisplayString(), true ) ); String details = mTrafficChannelCallEvent.getDetails(); @@ -572,13 +559,12 @@ public class ChannelState extends Module implements ICallEventProvider, if( frequency > 0 ) { - broadcast( new Metadata( MetadataType.FREQUENCY, - String.valueOf( frequency ), true ) ); + broadcast( new Metadata( MetadataType.FREQUENCY, String.valueOf( frequency ), true ) ); } /* Rebroadcast the allocation event so that the internal decoder states * can self-configure with the call event details */ - broadcast( allocationEvent ); + broadcast( mTrafficChannelCallEvent ); } /** @@ -660,7 +646,6 @@ public class ChannelState extends Module implements ICallEventProvider, if( State.CALL_STATES.contains( mState ) && mFadeTimeout <= System.currentTimeMillis() ) { - mState = State.FADE; processFadeState(); } else if( mState == State.FADE && diff --git a/src/module/decode/state/State.java b/src/module/decode/state/State.java index 1f1bc79b..3c9f5189 100644 --- a/src/module/decode/state/State.java +++ b/src/module/decode/state/State.java @@ -103,11 +103,8 @@ public enum State private String mDisplayValue; - public static final EnumSet CALL_STATES = - EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); - - public static final EnumSet IDLE_STATES = - EnumSet.of( IDLE, FADE ); + public static final EnumSet CALL_STATES = EnumSet.of( CALL, CONTROL, DATA, ENCRYPTED ); + public static final EnumSet IDLE_STATES = EnumSet.of( IDLE, FADE ); private State( String displayValue ) { diff --git a/src/module/decode/state/TrafficChannelManager.java b/src/module/decode/state/TrafficChannelManager.java index b78b9ea8..452d5680 100644 --- a/src/module/decode/state/TrafficChannelManager.java +++ b/src/module/decode/state/TrafficChannelManager.java @@ -7,7 +7,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledExecutorService; import module.Module; -import module.ProcessingChain; import module.decode.config.DecodeConfiguration; import module.decode.event.CallEvent; import module.decode.event.CallEvent.CallEventType; @@ -29,6 +28,7 @@ import controller.channel.ChannelEvent; import controller.channel.ChannelEvent.Event; import controller.channel.ChannelModel; import controller.channel.ChannelProcessingManager; +import controller.channel.TrafficChannelEvent; public class TrafficChannelManager extends Module implements ICallEventProvider, IDecoderStateEventListener @@ -148,7 +148,7 @@ public class TrafficChannelManager extends Module mTrafficChannelPool.add( channel ); } - /* If we have a configured channel, start it and track it */ + /* If we have a configured channel, update metadata */ if( channel != null ) { channel.setSourceConfiguration( new SourceConfigTuner( tunerChannel ) ); @@ -156,11 +156,7 @@ public class TrafficChannelManager extends Module channel.setSite( mSite ); channel.setName( channelNumber ); - mChannelModel.broadcast( new ChannelEvent( channel, - Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); - - mChannelModel.broadcast( new ChannelEvent( channel, - Event.REQUEST_ENABLE ) ); + mChannelModel.broadcast( new ChannelEvent( channel, Event.NOTIFICATION_CONFIGURATION_CHANGE ) ); } } @@ -201,35 +197,25 @@ public class TrafficChannelManager extends Module } else if( frequency > 0 ) { - Channel channel = getChannel( callEvent.getChannel(), - new TunerChannel( Type.TRAFFIC, frequency, - mDecodeConfiguration.getDecoderType() - .getChannelBandwidth() ) ); + Channel channel = getChannel( callEvent.getChannel(), new TunerChannel( Type.TRAFFIC, + frequency, mDecodeConfiguration.getDecoderType().getChannelBandwidth() ) ); if( channel != null ) { + TrafficChannelEvent trafficChannelEvent = + new TrafficChannelEvent( this, channel, Event.REQUEST_ENABLE, callEvent ); + + //Request to enable the channel + mChannelModel.broadcast( trafficChannelEvent ); + if( channel.getEnabled() ) { - ProcessingChain chain = mChannelProcessingManager - .getProcessingChain( channel ); - - if( chain != null ) - { - ChannelState state = chain.getChannelState(); - - /* Register as a listener to be notified when the call - * is completed */ - state.configureAsTrafficChannel( - new TrafficChannelStatusListener( this, - callEvent.getChannel() ), event ); - - /* Set state to call so that audio squelch state is correct */ - state.setState( State.CALL ); - } + mTrafficChannelsInUse.put( callEvent.getChannel(), channel ); } else { callEvent.setCallEventType( CallEventType.CALL_DETECT ); + callEvent.setDetails( "CHANNEL START REJECTED" ); } } else @@ -394,7 +380,7 @@ public class TrafficChannelManager extends Module if( channelNumber != null && mTrafficChannelsInUse.containsKey( channelNumber ) ) { Channel channel = mTrafficChannelsInUse.get( channelNumber ); - + mChannelModel.broadcast( new ChannelEvent( channel, Event.REQUEST_DISABLE ) ); mTrafficChannelsInUse.remove( channelNumber ); diff --git a/src/module/decode/state/TrafficChannelStatusListener.java b/src/module/decode/state/TrafficChannelStatusListener.java deleted file mode 100644 index e1b31a58..00000000 --- a/src/module/decode/state/TrafficChannelStatusListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package module.decode.state; - -public class TrafficChannelStatusListener -{ - private TrafficChannelManager mTrafficChannelManager; - private String mChannelNumber; - - public TrafficChannelStatusListener( TrafficChannelManager manager, String channelNumber ) - { - mTrafficChannelManager = manager; - mChannelNumber = channelNumber; - } - - public void callEnd() - { - if( mTrafficChannelManager != null ) - { - mTrafficChannelManager.callEnd( mChannelNumber ); - } - } -} diff --git a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java index 7920d9a1..63da95da 100644 --- a/src/module/demodulate/audio/DemodulatedAudioFilterModule.java +++ b/src/module/demodulate/audio/DemodulatedAudioFilterModule.java @@ -51,9 +51,8 @@ public class DemodulatedAudioFilterModule extends Module assert( stop > pass ); //TODO: change this to a band pass filter - mBandPassFilter = new RealFIRFilter_RB_RB( - FilterFactory.getLowPass( 48000, pass, stop, 60, - WindowType.HANNING, true ), 1.0f ); + mBandPassFilter = new RealFIRFilter_RB_RB( FilterFactory.getLowPass( 48000, pass, stop, 60, + WindowType.HANNING, true ), 1.0f ); mDCFilter.setListener( mBandPassFilter ); } diff --git a/src/module/demodulate/fm/FMDemodulatorModule.java b/src/module/demodulate/fm/FMDemodulatorModule.java index f96f9528..0b4033a0 100644 --- a/src/module/demodulate/fm/FMDemodulatorModule.java +++ b/src/module/demodulate/fm/FMDemodulatorModule.java @@ -37,6 +37,8 @@ import dsp.fm.FMDemodulator_CB; public class FMDemodulatorModule extends Module implements IComplexBufferListener, IUnFilteredRealBufferProvider { + private final static Logger mLog = LoggerFactory.getLogger( FMDemodulatorModule.class ); + private static final int SAMPLE_RATE = 48000; private ComplexFIRFilter_CB_CB mIQFilter; diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index cc5432b5..0861e303 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -614,7 +614,6 @@ public class SettingsManager implements Listener private void save() { - mLog.debug( "Saving settings" ); saveTunerConfigurationModel(); JAXBContext context = null; @@ -699,8 +698,7 @@ public class SettingsManager implements Listener if( Files.exists( settingsPath ) ) { - mLog.info( "SettingsManager - loading settings file [" + - settingsPath.toString() + "]" ); + mLog.info( "SettingsManager - loading settings file [" + settingsPath.toString() + "]" ); JAXBContext context = null; diff --git a/src/source/tuner/TunerChannelSource.java b/src/source/tuner/TunerChannelSource.java index 66fba403..ca4a9205 100644 --- a/src/source/tuner/TunerChannelSource.java +++ b/src/source/tuner/TunerChannelSource.java @@ -132,8 +132,7 @@ public class TunerChannelSource extends ComplexSource } else { - mLog.warn( "Attempt to start() and already running tuner channel " - + "source was ignored" ); + mLog.warn( "Attempt to start() and already running tuner channel source was ignored" ); } } @@ -162,8 +161,7 @@ public class TunerChannelSource extends ComplexSource } else { - mLog.warn( "Attempt to stop() and already stopped tuner channel " - + "source was ignored" ); + mLog.warn( "Attempt to stop() and already stopped tuner channel source was ignored" ); } } @@ -374,7 +372,7 @@ public class TunerChannelSource extends ComplexSource } } - mLog.debug( "Processed [" + mSampleBuffers.size() + "] in " + ( System.currentTimeMillis() - start ) ); +// mLog.debug( "Processed [" + mSampleBuffers.size() + "] in " + ( System.currentTimeMillis() - start ) ); mSampleBuffers.clear(); }