diff --git a/.classpath b/.classpath
index f5d9ad92..4d902b0c 100644
--- a/.classpath
+++ b/.classpath
@@ -5,7 +5,6 @@
-
@@ -18,5 +17,8 @@
+
+
+
diff --git a/imports/JLargeArrays-1.2.jar b/imports/JLargeArrays-1.2.jar
new file mode 100644
index 00000000..7be7a5da
Binary files /dev/null and b/imports/JLargeArrays-1.2.jar differ
diff --git a/imports/JTransforms-3.0.jar b/imports/JTransforms-3.0.jar
new file mode 100644
index 00000000..9cc2e3f3
Binary files /dev/null and b/imports/JTransforms-3.0.jar differ
diff --git a/imports/jtransforms-2.4.jar b/imports/jtransforms-2.4.jar
deleted file mode 100644
index 01bd519c..00000000
Binary files a/imports/jtransforms-2.4.jar and /dev/null differ
diff --git a/src/controller/ConfigurationControllerModel.java b/src/controller/ConfigurationControllerModel.java
index ab990a0b..bc24d0c2 100644
--- a/src/controller/ConfigurationControllerModel.java
+++ b/src/controller/ConfigurationControllerModel.java
@@ -37,7 +37,7 @@ import source.tuner.TunerGroupNode;
import source.tuner.TunerNode;
import source.tuner.TunerSelectionListener;
import controller.channel.Channel;
-import controller.channel.ChannelListener;
+import controller.channel.ChannelEventListener;
import controller.channel.ChannelNode;
import controller.site.Site;
import controller.system.System;
@@ -54,8 +54,8 @@ public class ConfigurationControllerModel extends DefaultTreeModel
private ResourceManager mResourceManager;
- private ArrayList mChannelConfigChangeListeners =
- new ArrayList();
+ private ArrayList mChannelConfigChangeListeners =
+ new ArrayList();
private ArrayList mTunerSelectionListeners =
new ArrayList();
diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java
index 3ddbc439..a0eaf0a2 100644
--- a/src/controller/ControllerPanel.java
+++ b/src/controller/ControllerPanel.java
@@ -18,22 +18,27 @@
package controller;
import java.awt.Color;
+import java.awt.Component;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import map.MapPanel;
import message.Message;
import net.miginfocom.swing.MigLayout;
import sample.Listener;
+import spectrum.ChannelSpectrumPanel;
import com.jidesoft.swing.JideSplitPane;
import com.jidesoft.swing.JideTabbedPane;
import controller.activity.CallEventPanel;
import controller.activity.MessageActivityPanel;
+import controller.channel.ChannelManager;
import controller.state.ChannelStateList;
public class ControllerPanel extends JPanel
@@ -46,7 +51,9 @@ public class ControllerPanel extends JPanel
private MessageActivityPanel mMessageActivityPanel =
new MessageActivityPanel();
-
+
+ private ChannelSpectrumPanel mChannelSpectrumPanel;
+
private JideTabbedPane mTabbedPane;
protected ConfigurationTreePanel mSystemControlViewPanel;
@@ -79,14 +86,41 @@ public class ControllerPanel extends JPanel
mSystemControlSplitPane.add( mSystemControlViewPanel );
mSystemControlSplitPane.add( mConfigurationEditor );
+ mChannelSpectrumPanel = new ChannelSpectrumPanel( mResourceManager );
+
//Tabbed View - configuration, calls, messages, map
mTabbedPane = new JideTabbedPane();
mTabbedPane.setFont( this.getFont() );
mTabbedPane.setForeground( Color.BLACK );
mTabbedPane.addTab( "Configuration", mSystemControlSplitPane );
- mTabbedPane.addTab( "Calls", mCallEventPanel );
+ mTabbedPane.addTab( "Channel Spectrum", mChannelSpectrumPanel );
+ mTabbedPane.addTab( "Events", mCallEventPanel );
mTabbedPane.addTab( "Messages", mMessageActivityPanel );
+ /**
+ * Change listener to enable/disable the channel spectrum display
+ * only when the tab is visible, and a channel has been selected
+ */
+ mTabbedPane.addChangeListener( new ChangeListener()
+ {
+ @Override
+ public void stateChanged( ChangeEvent event )
+ {
+ int index = mTabbedPane.getSelectedIndex();
+
+ Component component = mTabbedPane.getComponentAt( index );
+
+ if( component instanceof ChannelSpectrumPanel )
+ {
+ mChannelSpectrumPanel.setEnabled( true );
+ }
+ else
+ {
+ mChannelSpectrumPanel.setEnabled( false );
+ }
+ }
+ } );
+
/**
* Add mapping services and map panel to a new tab
*/
@@ -102,17 +136,13 @@ public class ControllerPanel extends JPanel
/* Channel state list */
mChannelStateList = new ChannelStateList( mResourceManager.getSettingsManager() );
- /* Add message activity panel to receive channel selection events so
- * that we can display the correct message listings */
- mResourceManager.getChannelManager().addListener( mMessageActivityPanel );
-
- /* Add call event panel to receive channel selection events so
- * that we can display the correct call event listings */
- mResourceManager.getChannelManager().addListener( mCallEventPanel );
-
- /* Register ChannelStateList as a listener on the channel manager to
- * receive channel starts and stops and deletes */
- mResourceManager.getChannelManager().addListener( mChannelStateList );
+ /* Register each of the components to receive channel events when the
+ * channels are selected or change */
+ ChannelManager channelManager = mResourceManager.getChannelManager();
+ channelManager.addListener( mCallEventPanel );
+ channelManager.addListener( mChannelStateList );
+ channelManager.addListener( mChannelSpectrumPanel );
+ channelManager.addListener( mMessageActivityPanel );
JScrollPane channelStateListScroll = new JScrollPane();
channelStateListScroll.getViewport().setView( mChannelStateList );
diff --git a/src/controller/activity/CallEvent.java b/src/controller/activity/CallEvent.java
index be62110f..ebf6de87 100644
--- a/src/controller/activity/CallEvent.java
+++ b/src/controller/activity/CallEvent.java
@@ -202,6 +202,7 @@ public abstract class CallEvent
PAGE( "Page" ),
REGISTER( "Register" ),
REGISTER_ESN( "ESN" ),
+ REQUEST( "Request" ),
SDM( "SDM" ),
STATUS( "Status" ),
UNKNOWN( "Unknown" );
diff --git a/src/controller/activity/CallEventPanel.java b/src/controller/activity/CallEventPanel.java
index 62846d98..2611ad54 100644
--- a/src/controller/activity/CallEventPanel.java
+++ b/src/controller/activity/CallEventPanel.java
@@ -23,10 +23,11 @@ import javax.swing.JTable;
import net.miginfocom.swing.MigLayout;
import controller.channel.Channel;
-import controller.channel.Channel.ChannelEvent;
-import controller.channel.ChannelListener;
+import controller.channel.ChannelEvent;
+import controller.channel.ChannelEvent.Event;
+import controller.channel.ChannelEventListener;
-public class CallEventPanel extends JPanel implements ChannelListener
+public class CallEventPanel extends JPanel implements ChannelEventListener
{
private static final long serialVersionUID = 1L;
@@ -46,31 +47,34 @@ public class CallEventPanel extends JPanel implements ChannelListener
}
@Override
- public void occurred( Channel channel, ChannelEvent event )
+ public void channelChanged( ChannelEvent event )
{
- if( event == ChannelEvent.CHANGE_SELECTED && channel.getSelected() )
+ if( event.getEvent() == Event.CHANGE_SELECTED &&
+ event.getChannel().isSelected() )
{
if( mDisplayedChannel == null ||
- ( mDisplayedChannel != null && mDisplayedChannel != channel ) )
+ ( mDisplayedChannel != null &&
+ mDisplayedChannel != event.getChannel() ) )
{
removeAll();
JScrollPane scroller = new JScrollPane(
- channel.getProcessingChain().getChannelState()
+ event.getChannel().getProcessingChain().getChannelState()
.getCallEventTable() );
add( scroller );
- mDisplayedChannel = channel;
+ mDisplayedChannel = event.getChannel();
revalidate();
repaint();
}
}
- else if( event == ChannelEvent.PROCESSING_STOPPED ||
- event == ChannelEvent.CHANNEL_DELETED )
+ else if( event.getEvent() == Event.PROCESSING_STOPPED ||
+ event.getEvent() == Event.CHANNEL_DELETED )
{
- if( mDisplayedChannel != null && mDisplayedChannel == channel )
+ if( mDisplayedChannel != null &&
+ mDisplayedChannel == event.getChannel() )
{
mDisplayedChannel = null;
removeAll();
diff --git a/src/controller/activity/MessageActivityPanel.java b/src/controller/activity/MessageActivityPanel.java
index 5783a19d..6be195de 100644
--- a/src/controller/activity/MessageActivityPanel.java
+++ b/src/controller/activity/MessageActivityPanel.java
@@ -23,10 +23,11 @@ import javax.swing.JTable;
import net.miginfocom.swing.MigLayout;
import controller.channel.Channel;
-import controller.channel.Channel.ChannelEvent;
-import controller.channel.ChannelListener;
+import controller.channel.ChannelEvent;
+import controller.channel.ChannelEvent.Event;
+import controller.channel.ChannelEventListener;
-public class MessageActivityPanel extends JPanel implements ChannelListener
+public class MessageActivityPanel extends JPanel implements ChannelEventListener
{
private static final long serialVersionUID = 1L;
@@ -44,31 +45,34 @@ public class MessageActivityPanel extends JPanel implements ChannelListener
}
@Override
- public void occurred( Channel channel, ChannelEvent event )
+ public void channelChanged( ChannelEvent event )
{
- if( event == ChannelEvent.CHANGE_SELECTED && channel.getSelected() )
+ if( event.getEvent() == Event.CHANGE_SELECTED &&
+ event.getChannel().isSelected() )
{
if( mDisplayedChannel == null ||
- ( mDisplayedChannel != null && mDisplayedChannel != channel ) )
+ ( mDisplayedChannel != null &&
+ mDisplayedChannel != event.getChannel() ) )
{
removeAll();
JScrollPane scroll =
- new JScrollPane( channel.getProcessingChain()
+ new JScrollPane( event.getChannel().getProcessingChain()
.getChannelState().getMessageActivityTable() );
add( scroll );
- mDisplayedChannel = channel;
+ mDisplayedChannel = event.getChannel();
revalidate();
repaint();
}
}
- else if( event == ChannelEvent.PROCESSING_STOPPED ||
- event == ChannelEvent.CHANNEL_DELETED )
+ else if( event.getEvent() == Event.PROCESSING_STOPPED ||
+ event.getEvent() == Event.CHANNEL_DELETED )
{
- if( mDisplayedChannel != null && mDisplayedChannel == channel )
+ if( mDisplayedChannel != null &&
+ mDisplayedChannel == event.getChannel() )
{
mDisplayedChannel = null;
removeAll();
diff --git a/src/controller/channel/Channel.java b/src/controller/channel/Channel.java
index ce35eef4..6dee242a 100644
--- a/src/controller/channel/Channel.java
+++ b/src/controller/channel/Channel.java
@@ -47,6 +47,7 @@ import source.tuner.TunerChannel;
import source.tuner.TunerChannel.Type;
import controller.ResourceManager;
import controller.activity.ActivitySummaryFrame;
+import controller.channel.ChannelEvent.Event;
import controller.config.Configuration;
import controller.site.Site;
import controller.state.ChannelState;
@@ -71,8 +72,8 @@ public class Channel extends Configuration
private static final boolean DISABLED = false;
private static final boolean BROADCAST_CHANGE = true;
- private CopyOnWriteArrayList mChannelListeners =
- new CopyOnWriteArrayList();
+ private CopyOnWriteArrayList mChannelListeners =
+ new CopyOnWriteArrayList();
private CopyOnWriteArrayList> mMessageListeners =
new CopyOnWriteArrayList>();
@@ -181,7 +182,7 @@ public class Channel extends Configuration
* Indicates if this channel has been selected for audio output
*/
@XmlTransient
- public boolean getSelected()
+ public boolean isSelected()
{
return mSelected;
}
@@ -195,7 +196,7 @@ public class Channel extends Configuration
mProcessingChain.getAudioOutput().setAudioPlaybackEnabled( selected );
}
- fireChannelEvent( ChannelEvent.CHANGE_SELECTED );
+ fireChannelEvent( Event.CHANGE_SELECTED );
}
public JMenu getContextMenu()
@@ -290,7 +291,7 @@ public class Channel extends Configuration
setEnabled( DISABLED, BROADCAST_CHANGE );
/* Broadcast channel deleted event */
- fireChannelEvent( ChannelEvent.CHANNEL_DELETED );
+ fireChannelEvent( Event.CHANNEL_DELETED );
}
/**
@@ -375,7 +376,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_SYSTEM );
+ fireChannelEvent( Event.CHANGE_SYSTEM );
}
}
@@ -397,7 +398,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_SITE );
+ fireChannelEvent( Event.CHANGE_SITE );
}
}
@@ -440,7 +441,7 @@ public class Channel extends Configuration
if( fireChannelEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_ENABLED );
+ fireChannelEvent( Event.CHANGE_ENABLED );
}
}
@@ -475,7 +476,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_ALIAS_LIST );
+ fireChannelEvent( Event.CHANGE_ALIAS_LIST );
}
}
@@ -534,7 +535,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_DECODER );
+ fireChannelEvent( Event.CHANGE_DECODER );
}
}
@@ -571,7 +572,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_SOURCE );
+ fireChannelEvent( Event.CHANGE_SOURCE );
}
}
@@ -608,7 +609,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_EVENT_LOGGER );
+ fireChannelEvent( Event.CHANGE_EVENT_LOGGER );
}
}
@@ -645,7 +646,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_RECORDER );
+ fireChannelEvent( Event.CHANGE_RECORDER );
}
}
@@ -676,7 +677,7 @@ public class Channel extends Configuration
if( fireChangeEvent )
{
- fireChannelEvent( ChannelEvent.CHANGE_NAME );
+ fireChannelEvent( Event.CHANGE_NAME );
}
}
@@ -715,7 +716,7 @@ public class Channel extends Configuration
mProcessingChain.start();
- fireChannelEvent( ChannelEvent.PROCESSING_STARTED );
+ fireChannelEvent( Event.PROCESSING_STARTED );
}
/* If this is a traffic channel, override the call fade timeout before
@@ -762,17 +763,17 @@ public class Channel extends Configuration
mSelected = false;
}
- fireChannelEvent( ChannelEvent.PROCESSING_STOPPED );
+ fireChannelEvent( Event.PROCESSING_STOPPED );
}
/**
* Broadcasts a channel change event to all registered listeners
*/
- public void fireChannelEvent( ChannelEvent event )
+ public void fireChannelEvent( Event event )
{
- for( ChannelListener listener: mChannelListeners )
+ for( ChannelEventListener listener: mChannelListeners )
{
- listener.occurred( this, event );
+ listener.channelChanged( new ChannelEvent( this, event ) );
}
}
@@ -780,17 +781,19 @@ public class Channel extends Configuration
* Adds a channel listener to receive all channel events from this channel
* and automatically sends the listener a channel add event.
*/
- public void addListener( ChannelListener listener )
+ public void addListener( ChannelEventListener listener )
{
if( !mChannelListeners.contains( listener ) )
{
mChannelListeners.add( listener );
- listener.occurred( this, ChannelEvent.CHANNEL_ADDED );
+ listener.channelChanged(
+ new ChannelEvent( this, Event.CHANNEL_ADDED ) );
}
else
{
- Log.error( "Channel - attempt to add already existing channel listener [" + listener.getClass() + "]" );
+ Log.error( "Channel - attempt to add already existing channel "
+ + "listener [" + listener.getClass() + "]" );
}
}
@@ -798,9 +801,9 @@ public class Channel extends Configuration
* 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 )
+ public void addListeners( List listeners )
{
- for( ChannelListener listener: listeners )
+ for( ChannelEventListener listener: listeners )
{
addListener( listener );
}
@@ -809,7 +812,7 @@ public class Channel extends Configuration
/**
* Removes a channel listener from receiving channel events from this channel
*/
- public void removeListener( ChannelListener listener )
+ public void removeListener( ChannelEventListener listener )
{
mChannelListeners.remove( listener );
}
@@ -884,28 +887,4 @@ public class Channel extends Configuration
{
return mTrafficChannels.containsKey( channelNumber );
}
-
- /**
- * Channel Events - used to specify channel events and changes to channel
- * configurations and settings.
- */
- public enum ChannelEvent
- {
- CHANGE_ALIAS_LIST,
- CHANGE_DECODER,
- CHANGE_ENABLED,
- CHANGE_EVENT_LOGGER,
- CHANGE_NAME,
- CHANGE_SITE,
- CHANGE_RECORDER,
- CHANGE_SELECTED,
- CHANGE_SOURCE,
- CHANGE_SYSTEM,
-
- CHANNEL_ADDED,
- CHANNEL_DELETED,
-
- PROCESSING_STARTED,
- PROCESSING_STOPPED,
- }
}
diff --git a/src/controller/channel/ChannelEvent.java b/src/controller/channel/ChannelEvent.java
new file mode 100644
index 00000000..df2fc15b
--- /dev/null
+++ b/src/controller/channel/ChannelEvent.java
@@ -0,0 +1,52 @@
+package controller.channel;
+
+public class ChannelEvent
+{
+ private Channel mChannel;
+ private Event mEvent;
+
+ /**
+ * ChannelEvent - event describing any changes to channels
+ * @param channel - channel that changed
+ * @param event - change event
+ */
+ public ChannelEvent( Channel channel, Event event )
+ {
+ mChannel = channel;
+ mEvent = event;
+ }
+
+ public Channel getChannel()
+ {
+ return mChannel;
+ }
+
+ public Event getEvent()
+ {
+ return mEvent;
+ }
+
+ /**
+ * Channel Events - used to specify channel events and changes to channel
+ * configurations and settings.
+ */
+ public enum Event
+ {
+ CHANGE_ALIAS_LIST,
+ CHANGE_DECODER,
+ CHANGE_ENABLED,
+ CHANGE_EVENT_LOGGER,
+ CHANGE_NAME,
+ CHANGE_SITE,
+ CHANGE_RECORDER,
+ CHANGE_SELECTED,
+ CHANGE_SOURCE,
+ CHANGE_SYSTEM,
+
+ CHANNEL_ADDED,
+ CHANNEL_DELETED,
+
+ PROCESSING_STARTED,
+ PROCESSING_STOPPED,
+ }
+}
diff --git a/src/controller/channel/ChannelListener.java b/src/controller/channel/ChannelEventListener.java
similarity index 83%
rename from src/controller/channel/ChannelListener.java
rename to src/controller/channel/ChannelEventListener.java
index c42324bd..7c5405db 100644
--- a/src/controller/channel/ChannelListener.java
+++ b/src/controller/channel/ChannelEventListener.java
@@ -17,16 +17,14 @@
******************************************************************************/
package controller.channel;
-import controller.channel.Channel.ChannelEvent;
-
/**
* Interface for receiving channel events. Each listener will receive
* notification of all channel events.
*/
-public interface ChannelListener
+public interface ChannelEventListener
{
/**
- * Notifies the listener that a channel has changed and what has changed
+ * Notifies the listener that a channel event has occurred
*/
- public void occurred( Channel channel, ChannelEvent event );
+ public void channelChanged( ChannelEvent event );
}
diff --git a/src/controller/channel/ChannelManager.java b/src/controller/channel/ChannelManager.java
index 2ca807fc..96dfbeb0 100644
--- a/src/controller/channel/ChannelManager.java
+++ b/src/controller/channel/ChannelManager.java
@@ -22,14 +22,13 @@ import java.util.ArrayList;
import message.Message;
import sample.Listener;
import controller.ResourceManager;
-import controller.channel.Channel.ChannelEvent;
-public class ChannelManager implements ChannelListener
+public class ChannelManager implements ChannelEventListener
{
private ArrayList mChannels = new ArrayList();
- private ArrayList mChannelListeners =
- new ArrayList();
+ private ArrayList mChannelListeners =
+ new ArrayList();
private ArrayList> mMessageListeners =
new ArrayList>();
@@ -45,18 +44,18 @@ public class ChannelManager implements ChannelListener
*/
@SuppressWarnings( "incomplete-switch" )
@Override
- public void occurred( Channel channel, ChannelEvent component )
+ public void channelChanged( ChannelEvent event )
{
- switch( component )
+ switch( event.getEvent() )
{
case CHANNEL_ADDED:
- if( !mChannels.contains( channel ) )
+ if( !mChannels.contains( event.getChannel() ) )
{
- mChannels.add( channel );
+ mChannels.add( event.getChannel() );
}
break;
case CHANNEL_DELETED:
- mChannels.remove( channel );
+ mChannels.remove( event.getChannel() );
break;
}
}
@@ -66,7 +65,7 @@ public class ChannelManager implements ChannelListener
* 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( ChannelListener listener )
+ public void addListener( ChannelEventListener listener )
{
mChannelListeners.add( listener );
@@ -80,7 +79,7 @@ public class ChannelManager implements ChannelListener
* Removes a channel listener from being automatically added to all channels
* to receive channel change events.
*/
- public void removeListener( ChannelListener listener )
+ public void removeListener( ChannelEventListener listener )
{
mChannelListeners.remove( listener );
@@ -94,7 +93,7 @@ public class ChannelManager implements ChannelListener
* Returns the list of channel change listeners that will be automatically
* added to all channels to receive channel change events
*/
- public ArrayList getChannelListeners()
+ public ArrayList getChannelListeners()
{
return mChannelListeners;
}
diff --git a/src/controller/channel/ChannelNode.java b/src/controller/channel/ChannelNode.java
index 665fca75..9bf26c0b 100644
--- a/src/controller/channel/ChannelNode.java
+++ b/src/controller/channel/ChannelNode.java
@@ -22,10 +22,8 @@ import java.text.DecimalFormat;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
-import log.Log;
import source.tuner.TunerChannel;
import controller.ConfigurableNode;
-import controller.channel.Channel.ChannelEvent;
import controller.site.SiteNode;
import controller.system.SystemNode;
@@ -33,7 +31,7 @@ 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 ChannelListener
+public class ChannelNode extends ConfigurableNode implements ChannelEventListener
{
private static final long serialVersionUID = 1L;
@@ -73,9 +71,9 @@ public class ChannelNode extends ConfigurableNode implements ChannelListener
* Listener method to receive channel changes from the underlying chnannel
*/
@Override
- public void occurred( Channel channel, ChannelEvent event )
+ public void channelChanged( ChannelEvent event )
{
- switch( event )
+ switch( event.getEvent() )
{
/* Refresh the node for each of these events */
case CHANGE_DECODER:
diff --git a/src/controller/state/ChannelStateList.java b/src/controller/state/ChannelStateList.java
index 6db9b475..5d976bd2 100644
--- a/src/controller/state/ChannelStateList.java
+++ b/src/controller/state/ChannelStateList.java
@@ -36,9 +36,9 @@ import settings.ColorSettingMenuItem;
import settings.ColorSettingResetMenuItem;
import settings.SettingsManager;
import controller.channel.Channel;
-import controller.channel.Channel.ChannelEvent;
import controller.channel.Channel.ChannelType;
-import controller.channel.ChannelListener;
+import controller.channel.ChannelEvent;
+import controller.channel.ChannelEventListener;
import decode.fleetsync2.FleetsyncChannelState;
import decode.fleetsync2.FleetsyncPanel;
import decode.ltrnet.LTRNetChannelState;
@@ -54,7 +54,7 @@ import decode.nbfm.NBFMPanel;
import decode.passport.PassportChannelState;
import decode.passport.PassportPanel;
-public class ChannelStateList extends JPanel implements ChannelListener
+public class ChannelStateList extends JPanel implements ChannelEventListener
{
private static final long serialVersionUID = 1L;
@@ -79,24 +79,25 @@ public class ChannelStateList extends JPanel implements ChannelListener
}
@Override
- public void occurred( Channel channel, ChannelEvent event )
+ public void channelChanged( ChannelEvent event )
{
- ChannelType type = channel.getChannelType();
+ ChannelType type = event.getChannel().getChannelType();
- switch( event )
+ switch( event.getEvent() )
{
case PROCESSING_STARTED:
if( type == ChannelType.STANDARD )
{
- channelStarted( channel );
+ channelStarted( event.getChannel() );
}
else if( type == ChannelType.TRAFFIC )
{
for( Channel parent: mDisplayedPanels.keySet() )
{
- if( parent.hasTrafficChannel( channel ) )
+ if( parent.hasTrafficChannel( event.getChannel() ) )
{
- mDisplayedPanels.get( parent ).trafficChannelAdded( channel );
+ mDisplayedPanels.get( parent )
+ .trafficChannelAdded( event.getChannel() );
validate();
}
}
@@ -105,7 +106,7 @@ public class ChannelStateList extends JPanel implements ChannelListener
case PROCESSING_STOPPED:
if( type == ChannelType.STANDARD )
{
- channelStopped( channel );
+ channelStopped( event.getChannel() );
}
/* Since we don't have a reference to the parent channel that
* owns the traffic channel, iterate all channels to find the
@@ -114,11 +115,12 @@ public class ChannelStateList extends JPanel implements ChannelListener
{
for( Channel control: mDisplayedPanels.keySet() )
{
- if( control.hasTrafficChannel( channel ) )
+ if( control.hasTrafficChannel( event.getChannel() ) )
{
- control.removeTrafficChannel( channel );
+ control.removeTrafficChannel( event.getChannel() );
- mDisplayedPanels.get( control ).trafficChannelDeleted( channel );
+ mDisplayedPanels.get( control )
+ .trafficChannelDeleted( event.getChannel() );
validate();
}
@@ -252,15 +254,17 @@ public class ChannelStateList extends JPanel implements ChannelListener
public void setSelectedChannel( ChannelStatePanel selectedPanel )
{
- boolean current = selectedPanel.getSelected();
-
+ //De-select any other selected panel
for( ChannelStatePanel panel: mDisplayedPanels.values() )
{
- //Turn selected off
- panel.setSelected( false );
+ if( panel != selectedPanel && panel.isSelected() )
+ {
+ panel.setSelected( false );
+ }
}
- selectedPanel.setSelected( !current );
+ //Toggle selection state
+ selectedPanel.setSelected( !selectedPanel.isSelected() );
}
public class ListSelectionListener implements MouseListener
diff --git a/src/controller/state/ChannelStatePanel.java b/src/controller/state/ChannelStatePanel.java
index 2ed59bd4..4c8e6b3f 100644
--- a/src/controller/state/ChannelStatePanel.java
+++ b/src/controller/state/ChannelStatePanel.java
@@ -163,9 +163,9 @@ public abstract class ChannelStatePanel extends JPanel
return null;
}
- public boolean getSelected()
+ public boolean isSelected()
{
- return mChannel.getSelected();
+ return mChannel.isSelected();
}
public void addPanel( final JPanel panel )
@@ -224,13 +224,13 @@ public abstract class ChannelStatePanel extends JPanel
public void setSelected( boolean selected )
{
- mChannel.setSelected( selected );
-
for( ChannelStatePanel traffic: getTrafficPanels().values() )
{
traffic.setSelected( false );
}
-
+
+ mChannel.setSelected( selected );
+
repaint();
}
@@ -293,7 +293,7 @@ public abstract class ChannelStatePanel extends JPanel
g2.drawLine( 0, getHeight() - 1, getWidth(), getHeight() - 1 );
/* Draw channel selected box */
- if( mChannel.getSelected() )
+ if( mChannel.isSelected() )
{
g2.setColor( mColorChannelSelected );
g2.drawRect( 1, 1, getWidth() - 2, getHeight() - 2 );
diff --git a/src/dsp/filter/FilterFactory.java b/src/dsp/filter/FilterFactory.java
index 2a9a1a4f..24f56fcc 100644
--- a/src/dsp/filter/FilterFactory.java
+++ b/src/dsp/filter/FilterFactory.java
@@ -22,8 +22,10 @@ import java.util.Set;
import java.util.TreeSet;
import log.Log;
+
+import org.jtransforms.fft.DoubleFFT_1D;
+
import dsp.filter.Window.WindowType;
-import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;
public class FilterFactory
{
diff --git a/src/dsp/filter/Window.java b/src/dsp/filter/Window.java
index eaf1588c..72e846b4 100644
--- a/src/dsp/filter/Window.java
+++ b/src/dsp/filter/Window.java
@@ -157,7 +157,7 @@ public class Window
*/
public static float[] apply( double[] coefficients, float[] samples )
{
- for( int x = 0; x < samples.length; x++ )
+ for( int x = 0; x < coefficients.length; x++ )
{
samples[ x ] = (float)( samples[ x ] * coefficients[ x ] );
}
@@ -177,7 +177,7 @@ public class Window
*/
public static double[] apply( double[] coefficients, double[] samples )
{
- for( int x = 0; x < samples.length; x++ )
+ for( int x = 0; x < coefficients.length; x++ )
{
samples[ x ] = samples[ x ] * coefficients[ x ];
}
@@ -231,9 +231,4 @@ public class Window
return sb.toString();
}
-
- public static void main( String[] args )
- {
- System.out.println( arrayToString( getBlackmanWindow( 31 ), false ) );
- }
}
diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java
index 6b08bc10..bef3bfca 100644
--- a/src/gui/SDRTrunk.java
+++ b/src/gui/SDRTrunk.java
@@ -145,8 +145,8 @@ public class SDRTrunk
JideSplitPane splitPane = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT );
splitPane.setDividerSize( 5 );
- splitPane.add( "Denny", mSpectralPanel );
- splitPane.add( "Denny2", mControllerPanel );
+ splitPane.add( mSpectralPanel );
+ splitPane.add( mControllerPanel );
mMainGui.add( splitPane, "cell 0 0,span,grow");
@@ -174,13 +174,13 @@ public class SDRTrunk
JMenuItem exitMenu = new JMenuItem( "Exit" );
exitMenu.addActionListener(
- new ActionListener()
- {
- public void actionPerformed( ActionEvent event )
- {
- System.exit( 0 );
- }
- }
+ new ActionListener()
+ {
+ public void actionPerformed( ActionEvent event )
+ {
+ System.exit( 0 );
+ }
+ }
);
fileMenu.add( exitMenu );
diff --git a/src/settings/IntegerSetting.java b/src/settings/IntegerSetting.java
new file mode 100644
index 00000000..f38541c5
--- /dev/null
+++ b/src/settings/IntegerSetting.java
@@ -0,0 +1,30 @@
+package settings;
+
+import javax.xml.bind.annotation.XmlAttribute;
+
+public class IntegerSetting extends Setting
+{
+ private int mValue;
+
+ public IntegerSetting()
+ {
+ }
+
+ public IntegerSetting( String name, int value )
+ {
+ super( name );
+
+ mValue = value;
+ }
+
+ @XmlAttribute
+ public int getValue()
+ {
+ return mValue;
+ }
+
+ public void setValue( int value )
+ {
+ mValue = value;
+ }
+}
diff --git a/src/settings/Settings.java b/src/settings/Settings.java
index 3144bbc7..d70cb8c2 100644
--- a/src/settings/Settings.java
+++ b/src/settings/Settings.java
@@ -45,6 +45,7 @@ import source.tuner.rtl.r820t.R820TTunerConfiguration;
FCD1TunerConfiguration.class,
FCD2TunerConfiguration.class,
FileSetting.class,
+ IntegerSetting.class,
MapViewSetting.class,
MapIcon.class,
R820TTunerConfiguration.class,
@@ -177,6 +178,20 @@ public class Settings
return null;
}
+
+ public IntegerSetting getIntegerSetting( String name )
+ {
+ for( Setting setting: mSettings )
+ {
+ if( setting instanceof IntegerSetting &&
+ ((IntegerSetting)setting).getName().contentEquals( name ) )
+ {
+ return (IntegerSetting)setting;
+ }
+ }
+
+ return null;
+ }
public ColorSetting getColorSetting( ColorSettingName name )
{
diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java
index 0fe0f0b5..940e6f71 100644
--- a/src/settings/SettingsManager.java
+++ b/src/settings/SettingsManager.java
@@ -143,6 +143,33 @@ public class SettingsManager
return mSettings.getSetting( name );
}
+ public IntegerSetting getIntegerSetting( String name, int defaultValue )
+ {
+ IntegerSetting setting = mSettings.getIntegerSetting( name );
+
+ if( setting == null )
+ {
+ setting = new IntegerSetting( name, defaultValue );
+
+ mSettings.addSetting( setting );
+
+ save();
+ }
+
+ return setting;
+ }
+
+ public void setIntegerSetting( String name, int value )
+ {
+ IntegerSetting setting = getIntegerSetting( name, value );
+
+ setting.setValue( value );
+
+ save();
+
+ broadcastSettingChange( setting );
+ }
+
/**
* Returns the current setting, or if the setting doesn't exist
* returns a newly created setting with the specified parameters
diff --git a/src/source/mixer/ShortAdapter.java b/src/source/mixer/ShortAdapter.java
index b8f0bf09..6497bb11 100644
--- a/src/source/mixer/ShortAdapter.java
+++ b/src/source/mixer/ShortAdapter.java
@@ -20,8 +20,6 @@ package source.mixer;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import source.Source.SampleType;
-
/**
* Converts 16-bit little endian byte data into an array of floats
*/
diff --git a/src/source/tuner/TunerClass.java b/src/source/tuner/TunerClass.java
index 9f9350e1..dae3665b 100644
--- a/src/source/tuner/TunerClass.java
+++ b/src/source/tuner/TunerClass.java
@@ -84,7 +84,7 @@ public enum TunerClass
return mVendorDescription + " " + mDeviceDescription;
}
- public TunerType getTunerClass()
+ public TunerType getTunerType()
{
return mTunerType;
}
diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java
index b4c4a064..7e34fd60 100644
--- a/src/source/tuner/TunerManager.java
+++ b/src/source/tuner/TunerManager.java
@@ -141,7 +141,7 @@ public class TunerManager
case FUNCUBE_DONGLE_PRO:
//Locate the matching mixer tuner dataline
MixerTunerDataLine fcd1Dataline =
- getMixerTunerDataLine( tunerClass.getTunerClass() );
+ getMixerTunerDataLine( tunerClass.getTunerType() );
if( fcd1Dataline != null )
{
FCD1TunerController fcd1Controller;
@@ -182,7 +182,7 @@ public class TunerManager
case FUNCUBE_DONGLE_PRO_PLUS:
//Locate the matching mixer tuner dataline
MixerTunerDataLine fcd2Dataline =
- getMixerTunerDataLine( tunerClass.getTunerClass() );
+ getMixerTunerDataLine( tunerClass.getTunerType() );
if( fcd2Dataline != null )
{
@@ -245,13 +245,15 @@ public class TunerManager
case TERRATEC_T_STICK_PLUS:
case TWINTECH_UT40:
case ZAAPA_ZTMINDVBZP:
- TunerType tunerType = tunerClass.getTunerClass();
+ TunerType tunerType = tunerClass.getTunerType();
if( tunerType == TunerType.RTL2832_VARIOUS )
{
tunerType = RTL2832TunerController.identifyTunerType( device );
}
+ Log.debug( "TunerManager - identified tuner class [" + tunerClass.toString() + "] with tuner type [" + tunerType.toString() + "]" );
+
switch( tunerType )
{
case ELONICS_E4000:
@@ -286,11 +288,13 @@ public class TunerManager
}
break;
case RAFAELMICRO_R820T:
+ Log.debug( "TunerManager - attempting to construct R820T tuner controller" );
try
{
R820TTunerController controller =
new R820TTunerController( device );
+ Log.debug( "TunerManager - init() R820T tuner controller" );
controller.init();
RTL2832Tuner rtlTuner =
@@ -302,8 +306,13 @@ public class TunerManager
if( config != null )
{
+ Log.debug( "TunerManager - applying tuner config to R820T tuner" );
rtlTuner.apply( config );
}
+ else
+ {
+ Log.debug( "TunerManager - R820T tuner config was null - not applied" );
+ }
mTuners.add( rtlTuner );
status = sLOADED;
@@ -315,6 +324,8 @@ public class TunerManager
status = sNOT_LOADED;
reason = "Error constructing R820T tuner "
+ "controller - " + se.getLocalizedMessage();
+
+ Log.error( "TunerManager - error constructing tuner", se );
}
break;
case FITIPOWER_FC0012:
@@ -324,8 +335,8 @@ public class TunerManager
default:
status = sNOT_LOADED;
reason = "SDRTRunk doesn't currently support RTL2832 "
- + "Dongle with [" + tunerType.toString() +
- "] tuner";
+ + "Dongle with [" + tunerType.toString() +
+ "] tuner for tuner class[" + tunerClass.toString() + "]";
break;
}
break;
diff --git a/src/source/tuner/rtl/RTL2832SampleAdapter.java b/src/source/tuner/rtl/RTL2832SampleAdapter.java
index 3ec92322..d11fdfd6 100644
--- a/src/source/tuner/rtl/RTL2832SampleAdapter.java
+++ b/src/source/tuner/rtl/RTL2832SampleAdapter.java
@@ -17,7 +17,6 @@
******************************************************************************/
package source.tuner.rtl;
-import source.Source.SampleType;
import source.mixer.SampleAdapter;
public class RTL2832SampleAdapter extends SampleAdapter
@@ -26,17 +25,15 @@ public class RTL2832SampleAdapter extends SampleAdapter
/**
* Creates a static lookup table that converts the 8-bit valued range
- * from 0 - 255 into scaled float values of -128 to 0 to 127
+ * from 0 - 255 into scaled float values of -32768.0 to 0 to 32512.0
*/
static
{
mLOOKUP_VALUES = new float[ 256 ];
- float scale = (float)( 1.0f / 127.0f );
-
for( int x = 0; x < 256; x++ )
{
- mLOOKUP_VALUES[ x ] = (float)( x - 128 ) * scale;
+ mLOOKUP_VALUES[ x ] = (float)( x - 128 ) * 256.0f;
}
mLOOKUP_VALUES[ 128 ] = 0.0000001f;
diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java
index c10c49ae..f9e3cedb 100644
--- a/src/source/tuner/rtl/RTL2832TunerController.java
+++ b/src/source/tuner/rtl/RTL2832TunerController.java
@@ -412,7 +412,7 @@ public abstract class RTL2832TunerController extends TunerController
tunerClass = TunerType.FITIPOWER_FC0012;
}
- Log.debug( "RTL2832 - disabling I2C repeater" );
+ Log.debug( "RTL2832 - disabling I2C repeater -- detected tuner type:" + tunerClass.toString() );
enableI2CRepeater( device, false );
diff --git a/src/spectrum/ChannelOverlayPanel.java b/src/spectrum/ChannelOverlayPanel.java
new file mode 100644
index 00000000..145edb10
--- /dev/null
+++ b/src/spectrum/ChannelOverlayPanel.java
@@ -0,0 +1,264 @@
+/*******************************************************************************
+ * 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 spectrum;
+
+import java.awt.Color;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Point;
+import java.awt.RenderingHints;
+import java.awt.geom.Line2D;
+import java.awt.geom.Rectangle2D;
+import java.text.DecimalFormat;
+
+import javax.swing.JPanel;
+
+import settings.ColorSetting;
+import settings.ColorSetting.ColorSettingName;
+import settings.Setting;
+import settings.SettingChangeListener;
+import controller.ResourceManager;
+
+public class ChannelOverlayPanel extends JPanel implements SettingChangeListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private static DecimalFormat sFORMAT = new DecimalFormat( "000.0" );
+ private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.0" );
+ private double mBandwidth = 6000.0d;
+ private Point mCursorLocation = new Point( 0, 0 );
+ private boolean mCursorVisible = false;
+
+ /**
+ * Colors used by this component
+ */
+ private Color mColorSpectrumBackground;
+ private Color mColorSpectrumCursor;
+ private Color mColorSpectrumLine;
+
+ //Defines the offset at the bottom of the spectral display to account for
+ //the frequency labels
+ private float mSpectrumInset = 20.0f;
+
+ private ResourceManager mResourceManager;
+
+ /**
+ * Translucent overlay panel for displaying channel configurations,
+ * processing channels, selected channels, frequency labels and lines, and
+ * a cursor with a frequency readout.
+ */
+ public ChannelOverlayPanel( ResourceManager resourceManager )
+ {
+ mResourceManager = resourceManager;
+
+ //Set the background transparent, so the spectrum display can be seen
+ setOpaque( false );
+
+ //Fetch color settings from settings manager
+ setColors();
+ }
+
+ public void dispose()
+ {
+ mResourceManager = null;
+ }
+
+ public void setCursorLocation( Point point )
+ {
+ mCursorLocation = point;
+
+ repaint();
+ }
+
+ public void setCursorVisible( boolean visible )
+ {
+ mCursorVisible = visible;
+
+ repaint();
+ }
+
+ /**
+ * Fetches the color settings from the settings manager
+ */
+ private void setColors()
+ {
+ mColorSpectrumCursor = getColor( ColorSettingName.SPECTRUM_CURSOR );
+
+ mColorSpectrumLine = getColor( ColorSettingName.SPECTRUM_LINE );
+
+ mColorSpectrumBackground =
+ getColor( ColorSettingName.SPECTRUM_BACKGROUND );
+ }
+
+ /**
+ * Fetches a named color setting from the settings manager. If the setting
+ * doesn't exist, creates the setting using the defaultColor
+ */
+ private Color getColor( ColorSettingName name )
+ {
+ ColorSetting setting =
+ mResourceManager.getSettingsManager()
+ .getColorSetting( name );
+
+ return setting.getColor();
+ }
+
+ /**
+ * Monitors for setting changes. Colors can be changed by external actions
+ * and will automatically update in this class
+ */
+ @Override
+ public void settingChanged( Setting setting )
+ {
+ if( setting instanceof ColorSetting )
+ {
+ ColorSetting colorSetting = (ColorSetting)setting;
+
+ switch( colorSetting.getColorSettingName() )
+ {
+ case SPECTRUM_BACKGROUND:
+ mColorSpectrumBackground = colorSetting.getColor();
+ break;
+ case SPECTRUM_CURSOR:
+ mColorSpectrumCursor = colorSetting.getColor();
+ break;
+ case SPECTRUM_LINE:
+ mColorSpectrumLine = colorSetting.getColor();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Renders the channel configs, lines, labels, and cursor
+ */
+ @Override
+ public void paintComponent( Graphics g )
+ {
+ super.paintComponent( g );
+
+ Graphics2D graphics = (Graphics2D) g;
+ graphics.setBackground( mColorSpectrumBackground );
+
+ RenderingHints renderHints =
+ new RenderingHints( RenderingHints.KEY_ANTIALIASING,
+ RenderingHints.VALUE_ANTIALIAS_ON );
+
+ renderHints.put( RenderingHints.KEY_RENDERING,
+ RenderingHints.VALUE_RENDER_QUALITY );
+
+ graphics.setRenderingHints( renderHints );
+
+ drawFrequencies( graphics );
+ drawCursor( graphics );
+ }
+
+ /**
+ * Draws a cursor on the panel, whenever the mouse is hovering over the
+ * panel
+ */
+ private void drawCursor( Graphics2D graphics )
+ {
+ if( mCursorVisible )
+ {
+ drawFrequencyLine( graphics,
+ mCursorLocation.x,
+ mColorSpectrumCursor );
+
+ double value = mBandwidth *
+ ( mCursorLocation.getX() / getSize().getWidth() );
+
+ String frequency = sCURSOR_FORMAT.format( value );
+
+ graphics.drawString( frequency ,
+ mCursorLocation.x + 5,
+ mCursorLocation.y );
+ }
+ }
+
+ /**
+ * Draws the frequency lines and labels every 10kHz
+ */
+ private void drawFrequencies( Graphics2D graphics )
+ {
+ drawFrequencyLine( graphics, getAxisFromFrequency( 1000 ), Color.LIGHT_GRAY );
+ drawFrequencyLineAndLabel( graphics, 2000 );
+ drawFrequencyLine( graphics, getAxisFromFrequency( 3000 ), Color.LIGHT_GRAY );
+ drawFrequencyLineAndLabel( graphics, 4000 );
+ drawFrequencyLine( graphics, getAxisFromFrequency( 5000 ), Color.LIGHT_GRAY );
+// drawFrequencyLineAndLabel( graphics, 6000 );
+// drawFrequencyLine( graphics, getAxisFromFrequency( 7000 ), Color.LIGHT_GRAY );
+// drawFrequencyLineAndLabel( graphics, 8000 );
+// drawFrequencyLine( graphics, getAxisFromFrequency( 9000 ), Color.LIGHT_GRAY );
+// drawFrequencyLineAndLabel( graphics, 10000 );
+// drawFrequencyLine( graphics, getAxisFromFrequency( 11000 ), Color.LIGHT_GRAY );
+ }
+
+ private float getAxisFromFrequency( long frequency )
+ {
+ return (float)( getSize().getWidth() * ( (double)frequency / mBandwidth ) );
+ }
+
+ /**
+ * Draws a vertical line and a corresponding frequency label at the bottom
+ */
+ private void drawFrequencyLineAndLabel( Graphics2D graphics, long frequency )
+ {
+ float xAxis = (float)( getSize().getWidth() *
+ ( (double)frequency / mBandwidth ) );
+
+ drawFrequencyLine( graphics, xAxis, mColorSpectrumLine );
+
+ graphics.setColor( mColorSpectrumLine );
+
+ drawFrequencyLabel( graphics, xAxis, frequency );
+ }
+
+ /**
+ * Draws a vertical line at the xaxis
+ */
+ private void drawFrequencyLine( Graphics2D graphics, float xaxis, Color color )
+ {
+ graphics.setColor( color );
+
+ graphics.draw( new Line2D.Float( xaxis, 0,
+ xaxis, (float)(getSize().getHeight()) - mSpectrumInset ) );
+ }
+
+ /**
+ * Draws a frequency label at the x-axis position, at the bottom of the panel
+ */
+ private void drawFrequencyLabel( Graphics2D graphics,
+ float xaxis,
+ long frequency )
+ {
+ String label = sFORMAT.format( (float)frequency );
+
+ FontMetrics fontMetrics = graphics.getFontMetrics( this.getFont() );
+
+ Rectangle2D rect = fontMetrics.getStringBounds( label, graphics );
+
+ float offset = (float)rect.getWidth() / 2;
+
+ graphics.drawString( label, xaxis - offset,
+ (float)getSize().getHeight() - ( mSpectrumInset * 0.2f ) );
+ }
+
+ public void settingDeleted( Setting setting ) {}
+}
diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java
new file mode 100644
index 00000000..1ef4a716
--- /dev/null
+++ b/src/spectrum/ChannelSpectrumPanel.java
@@ -0,0 +1,520 @@
+package spectrum;
+
+import java.awt.Component;
+import java.awt.event.ComponentEvent;
+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;
+import javax.swing.JMenu;
+import javax.swing.JPanel;
+import javax.swing.JPopupMenu;
+import javax.swing.SwingUtilities;
+
+import net.miginfocom.swing.MigLayout;
+import sample.Listener;
+import settings.ColorSetting.ColorSettingName;
+import settings.ColorSettingMenuItem;
+import settings.Setting;
+import settings.SettingChangeListener;
+import settings.SettingsManager;
+import source.Source.SampleType;
+import spectrum.converter.DFTResultsConverter;
+import spectrum.converter.RealDecibelConverter;
+import spectrum.menu.AmplificationItem;
+import spectrum.menu.AveragingItem;
+import spectrum.menu.BaselineItem;
+import spectrum.menu.FFTWidthItem;
+import spectrum.menu.FFTWindowTypeItem;
+import spectrum.menu.FrameRateItem;
+import controller.ResourceManager;
+import controller.channel.Channel;
+import controller.channel.ChannelEvent;
+import controller.channel.ChannelEventListener;
+import dsp.filter.Filters;
+import dsp.filter.FloatHalfBandFilter;
+import dsp.filter.Window.WindowType;
+
+public class ChannelSpectrumPanel extends JPanel
+ implements ChannelEventListener,
+ Listener,
+ SettingChangeListener,
+ SpectralDisplayAdjuster
+{
+ private static final long serialVersionUID = 1L;
+ private static final String CHANNEL_SPECTRUM_AVERAGING_SIZE =
+ "channel_spectrum_averaging_size";
+
+ private ResourceManager mResourceManager;
+ private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.FLOAT );
+ private DFTResultsConverter mDFTConverter = new RealDecibelConverter();
+ private JLayeredPane mLayeredPane;
+ private SpectrumPanel mSpectrumPanel;
+ private ChannelOverlayPanel mOverlayPanel;
+
+ private Channel mCurrentChannel;
+
+ private int mSpectrumAveragingSize;
+ private int mSampleBufferSize = 2400;
+ private Float[] mSamples = new Float[ mSampleBufferSize ];
+ private int mSamplePointer = 0;
+
+ private DecimatingSampleAssembler mDecimatingSampleAssembler;
+
+ private AtomicBoolean mEnabled = new AtomicBoolean();
+
+ public ChannelSpectrumPanel( ResourceManager resourceManager )
+ {
+ mResourceManager = resourceManager;
+ mResourceManager.getSettingsManager().addListener( this );
+ mSpectrumPanel = new SpectrumPanel( mResourceManager );
+ mOverlayPanel = new ChannelOverlayPanel( mResourceManager );
+
+
+ mSpectrumAveragingSize = mResourceManager.getSettingsManager()
+ .getIntegerSetting( CHANNEL_SPECTRUM_AVERAGING_SIZE, 10 ).getValue();
+
+ mSpectrumPanel.setAveraging( mSpectrumAveragingSize );
+
+ mDFTProcessor.addConverter( mDFTConverter );
+ mDFTConverter.addListener( mSpectrumPanel );
+
+ mDecimatingSampleAssembler = new DecimatingSampleAssembler( mDFTProcessor );
+
+ /* Set the DFTProcessor to the decimated 24kHz sample rate */
+ mDFTProcessor.frequencyChanged( 0, 24000 );
+
+ initGui();
+ }
+
+ public void dispose()
+ {
+ setEnabled( false );
+
+ mDFTProcessor.dispose();
+
+ mResourceManager = null;
+ mCurrentChannel = null;
+ mDFTProcessor = null;
+ mSpectrumPanel = null;
+ }
+
+ public void setFrameRate( int framesPerSecond )
+ {
+ mSampleBufferSize = (int)( 48000 / framesPerSecond );
+
+ mDFTProcessor.setFrameRate( framesPerSecond );
+ }
+
+ private void initGui()
+ {
+ setLayout( new MigLayout( "insets 0 0 0 0 ",
+ "[grow,fill]",
+ "[grow,fill]") );
+
+ mLayeredPane = new JLayeredPane();
+ mLayeredPane.addComponentListener( new ResizeListener() );
+
+ MouseEventProcessor mouser = new MouseEventProcessor();
+
+ mOverlayPanel.addMouseListener( mouser );
+ mOverlayPanel.addMouseMotionListener( mouser );
+
+ mLayeredPane.add( mSpectrumPanel, new Integer( 0 ), 0 );
+ mLayeredPane.add( mOverlayPanel, new Integer( 1 ), 0 );
+
+ add( mLayeredPane );
+ }
+
+ public void setEnabled( boolean enabled )
+ {
+ if( enabled && mEnabled.compareAndSet( false, true ) )
+ {
+ start();
+ }
+ else if( !enabled && mEnabled.compareAndSet( true, false ) )
+ {
+ stop();
+ }
+ }
+
+ @Override
+ @SuppressWarnings( "incomplete-switch" )
+ public void channelChanged( ChannelEvent event )
+ {
+ switch( event.getEvent() )
+ {
+ case CHANGE_SELECTED:
+ if( !event.getChannel().isSelected() )
+ {
+ stop();
+
+ mCurrentChannel = null;
+ }
+ else
+ {
+ mCurrentChannel = event.getChannel();
+
+ if( mEnabled.get() )
+ {
+ start();
+ }
+ }
+ if( event.getChannel().isSelected() &&
+ mCurrentChannel != event.getChannel() )
+ {
+ stop();
+
+ mCurrentChannel = event.getChannel();
+
+ start();
+ }
+ break;
+ case CHANNEL_DELETED:
+ if( event.getChannel() == mCurrentChannel )
+ {
+ if( mEnabled.get() )
+ {
+ stop();
+ }
+
+ mCurrentChannel = null;
+ }
+ break;
+ case CHANGE_ENABLED:
+ if( event.getChannel() == mCurrentChannel &&
+ !event.getChannel().isProcessing() )
+ {
+ stop();
+ mCurrentChannel = null;
+ }
+ break;
+ }
+ }
+
+ private void start()
+ {
+ if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() )
+ {
+ mCurrentChannel.getProcessingChain().addFloatListener( this );
+ mDFTProcessor.start();
+ }
+ }
+
+ private void stop()
+ {
+
+ if( mCurrentChannel != null && mCurrentChannel.isProcessing() )
+ {
+ mCurrentChannel.getProcessingChain().removeFloatListener( this );
+ }
+
+ mDFTProcessor.stop();
+
+ mSpectrumPanel.clearSpectrum();
+ }
+
+ @Override
+ public void settingChanged( Setting setting )
+ {
+ if( mSpectrumPanel != null )
+ {
+ mSpectrumPanel.settingChanged( setting );
+ }
+ if( mOverlayPanel != null )
+ {
+ mOverlayPanel.settingChanged( setting );
+ }
+ }
+
+ @Override
+ public void settingDeleted( Setting setting )
+ {
+ if( mSpectrumPanel != null )
+ {
+ mSpectrumPanel.settingDeleted( setting );
+ }
+
+ if( mOverlayPanel != null )
+ {
+ mOverlayPanel.settingDeleted( setting );
+ }
+ }
+
+ @Override
+ public void receive( Float sample )
+ {
+ mDecimatingSampleAssembler.receive( sample );
+ }
+
+ /**
+ * Monitors the sizing of the layered pane and resizes the spectrum and
+ * channel panels whenever the layered pane is resized
+ */
+ public class ResizeListener implements ComponentListener
+ {
+ @Override
+ public void componentResized( ComponentEvent e )
+ {
+ Component c = e.getComponent();
+
+ mSpectrumPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
+ mOverlayPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
+ }
+
+ @Override
+ public void componentHidden( ComponentEvent arg0 ) {}
+ @Override
+ public void componentMoved( ComponentEvent arg0 ) {}
+ @Override
+ public void componentShown( ComponentEvent arg0 ) {}
+ }
+
+ /**
+ * Mouse event handler for the channel panel.
+ */
+ public class MouseEventProcessor implements MouseMotionListener, MouseListener
+ {
+ @Override
+ public void mouseMoved( MouseEvent event )
+ {
+ update( event );
+ }
+ @Override
+ public void mouseDragged( MouseEvent event )
+ {
+ update( event );
+ }
+
+ private void update( MouseEvent event )
+ {
+ if( event.getComponent() == mOverlayPanel )
+ {
+ mOverlayPanel.setCursorLocation( event.getPoint() );
+ }
+ }
+
+ @Override
+ public void mouseEntered( MouseEvent e )
+ {
+ if( e.getComponent() == mOverlayPanel )
+ {
+ mOverlayPanel.setCursorVisible( true );
+ }
+ }
+
+ @Override
+ public void mouseExited( MouseEvent e )
+ {
+ mOverlayPanel.setCursorVisible( false );
+ }
+
+ /**
+ * Displays the context menu.
+ */
+ @Override
+ public void mouseClicked( MouseEvent event )
+ {
+ if( SwingUtilities.isRightMouseButton( event ) )
+ {
+ JPopupMenu contextMenu = new JPopupMenu();
+
+ /**
+ * Color Menus
+ */
+ JMenu colorMenu = new JMenu( "Color" );
+
+ SettingsManager sm = mResourceManager.getSettingsManager();
+
+ colorMenu.add( new ColorSettingMenuItem( sm,
+ ColorSettingName.SPECTRUM_CURSOR ) );
+
+ colorMenu.add( new ColorSettingMenuItem( sm,
+ ColorSettingName.SPECTRUM_LINE ) );
+
+ colorMenu.add( new ColorSettingMenuItem( sm,
+ ColorSettingName.SPECTRUM_BACKGROUND ) );
+
+ colorMenu.add( new ColorSettingMenuItem( sm,
+ ColorSettingName.SPECTRUM_GRADIENT_BOTTOM ) );
+
+ colorMenu.add( new ColorSettingMenuItem( sm,
+ ColorSettingName.SPECTRUM_GRADIENT_TOP ) );
+
+ contextMenu.add( colorMenu );
+
+ /**
+ * Display items: fft and frame rate
+ */
+ JMenu displayMenu = new JMenu( "Display" );
+ contextMenu.add( displayMenu );
+
+ /**
+ * Amplification menu
+ */
+ JMenu amplificationMenu = new JMenu( "Amplification" );
+ amplificationMenu.add(
+ new AmplificationItem( ChannelSpectrumPanel.this, 50 ) );
+ displayMenu.add( amplificationMenu );
+
+ /**
+ * Averaging menu
+ */
+ JMenu averagingMenu = new JMenu( "Averaging" );
+ averagingMenu.add(
+ new AveragingItem( ChannelSpectrumPanel.this, 4 ) );
+ displayMenu.add( averagingMenu );
+
+ /**
+ * Baseline menu
+ */
+ JMenu baselineMenu = new JMenu( "Baseline" );
+ baselineMenu.add(
+ new BaselineItem( ChannelSpectrumPanel.this, 50 ) );
+ displayMenu.add( baselineMenu );
+
+ /**
+ * FFT width
+ */
+ JMenu fftWidthMenu = new JMenu( "FFT Width" );
+ displayMenu.add( fftWidthMenu );
+
+ for( FFTWidth width: FFTWidth.values() )
+ {
+ fftWidthMenu.add( new FFTWidthItem( mDFTProcessor, width ) );
+ }
+
+ /**
+ * DFT Processor Frame Rate
+ */
+ JMenu frameRateMenu = new JMenu( "Frame Rate" );
+ displayMenu.add( frameRateMenu );
+
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 14 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 16 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 18 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 20 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 25 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 30 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 40 ) );
+ frameRateMenu.add( new FrameRateItem( mDFTProcessor, 50 ) );
+
+ /**
+ * FFT Window Type
+ */
+ JMenu fftWindowType = new JMenu( "Window Type" );
+ displayMenu.add( fftWindowType );
+
+ for( WindowType type: WindowType.values() )
+ {
+ fftWindowType.add(
+ new FFTWindowTypeItem( mDFTProcessor, type ) );
+ }
+
+ if( contextMenu != null )
+ {
+ contextMenu.show( mOverlayPanel,
+ event.getX(),
+ event.getY() );
+ }
+ }
+ }
+
+ @Override
+ public void mousePressed( MouseEvent e ) {}
+ @Override
+ public void mouseReleased( MouseEvent e ) {}
+ }
+
+ @Override
+ public int getAmplification()
+ {
+ return mSpectrumPanel.getAmplification();
+ }
+
+ @Override
+ public void setAmplification( int amplification )
+ {
+ mSpectrumPanel.setAmplification( amplification );
+ }
+
+ @Override
+ public int getAveraging()
+ {
+ return mSpectrumPanel.getAveraging();
+ }
+
+ @Override
+ public void setAveraging( int averaging )
+ {
+ mSpectrumPanel.setAveraging( averaging );
+ }
+
+ @Override
+ public int getBaseline()
+ {
+ return mSpectrumPanel.getBaseline();
+ }
+
+ @Override
+ public void setBaseline( int baseline )
+ {
+ mSpectrumPanel.setBaseline( baseline );
+ }
+
+ public class DecimatingSampleAssembler implements Listener
+ {
+ private FloatHalfBandFilter mDecimationFilter = new FloatHalfBandFilter(
+ Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
+
+ private SampleAssembler mSampleAssembler;
+
+ public DecimatingSampleAssembler( Listener listener )
+ {
+ mSampleAssembler = new SampleAssembler( listener );
+ mDecimationFilter.setListener( mSampleAssembler );
+ }
+
+ @Override
+ public void receive( Float sample )
+ {
+ mDecimationFilter.receive( sample );
+ }
+ }
+
+ public class SampleAssembler implements Listener
+ {
+ private Listener mListener;
+
+ public SampleAssembler( Listener listener )
+ {
+ mListener = listener;
+ }
+
+ @Override
+ public void receive( Float sample )
+ {
+ mSamples[ mSamplePointer++ ] = sample;
+
+ if( mSamplePointer >= mSamples.length )
+ {
+ if( mEnabled.get() )
+ {
+ mListener.receive( Arrays.copyOf( mSamples,
+ mSamples.length ) );
+ }
+
+ mSamplePointer = 0;
+
+ if( mSamples.length != mSampleBufferSize )
+ {
+ mSamples = new Float[ mSampleBufferSize ];
+ }
+ }
+ }
+ }
+}
diff --git a/src/spectrum/DFTProcessor.java b/src/spectrum/DFTProcessor.java
index 9b5c9b66..c9a68890 100644
--- a/src/spectrum/DFTProcessor.java
+++ b/src/spectrum/DFTProcessor.java
@@ -27,24 +27,26 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import log.Log;
+
+import org.jtransforms.fft.FloatFFT_1D;
+
import sample.Listener;
import source.Source;
import source.Source.SampleType;
import source.tuner.FrequencyChangeListener;
+import spectrum.converter.DFTResultsConverter;
import dsp.filter.Window;
import dsp.filter.Window.WindowType;
-import edu.emory.mathcs.jtransforms.fft.FloatFFT_1D;
/**
* Processes both complex samples or float samples and dispatches a float array
* of DFT results, using configurable fft size and output dispatch timelines.
*/
public class DFTProcessor implements Listener,
- DFTResultsProvider,
FrequencyChangeListener
{
- private CopyOnWriteArrayList mListeners =
- new CopyOnWriteArrayList();
+ private CopyOnWriteArrayList mListeners =
+ new CopyOnWriteArrayList();
private ArrayBlockingQueue mQueue =
new ArrayBlockingQueue( 200 );
@@ -56,14 +58,16 @@ public class DFTProcessor implements Listener,
private FFTWidth mNewFFTWidth = FFTWidth.FFT04096;
private double[] mWindow;
- private WindowType mWindowType = Window.WindowType.HANNING;
+ private WindowType mWindowType = Window.WindowType.BLACKMAN;
private FloatFFT_1D mFFT = new FloatFFT_1D( mFFTWidth.getWidth() );
private int mFrameRate;
private int mSampleRate;
- private float mOverlapPerFrame;
- private float mOverlapResidual;
+ private int mFFTFloatsPerFrame;
+ private float mNewFloatsPerFrame;
+ private float mNewFloatsPerFrameToConsume;
+ private float mNewFloatResidual;
private float[] mPreviousFrame = new float[ 8192 ];
private Float[] mCurrentBuffer;
@@ -81,17 +85,7 @@ public class DFTProcessor implements Listener,
public void dispose()
{
- try
- {
- clearScheduler();
- }
- catch ( InterruptedException e )
- {
- Log.error( "DFTProcessor - exception shutting down processor - " +
- e.getLocalizedMessage() );
- }
-
- mScheduler.shutdown();
+ stop();
mListeners.clear();
mQueue.clear();
@@ -165,54 +159,56 @@ public class DFTProcessor implements Listener,
+ "more than 1000 times per second -- requested setting:"
+ framesPerSecond );
}
- else
- {
- mFrameRate = framesPerSecond;
- calculateOverlap();
+ mFrameRate = framesPerSecond;
- /**
- * Shutdown the scheduler and clear out any remaining tasks
- */
- try
- {
- clearScheduler();
- }
- catch ( InterruptedException e )
- {
- Log.error( "DFTProcessor - exception while awaiting shutdown of "
- + "calculation scheduler for reset" );
- }
+ calculateConsumptionRate();
- /**
- * Reset the scheduler
- */
- mScheduler = Executors.newScheduledThreadPool(1);
-
- /**
- * Schedule the DFT to run calculations at a fixed rate
- */
- int initialDelay = 0;
- int period = (int)( 1000 / mFrameRate );
- TimeUnit unit = TimeUnit.MILLISECONDS;
-
- mScheduler.scheduleAtFixedRate( new DFTCalculationTask(),
- initialDelay, period, unit );
-
- }
+ restart();
}
- /**
- * Shuts down the scheduler and blocks up to 100 milliseconds until all
- * tasks have completed.
- */
- private void clearScheduler() throws InterruptedException
+ public void start()
{
- mScheduler.shutdown();
+ /**
+ * Reset the scheduler
+ */
+ mScheduler = Executors.newScheduledThreadPool(1);
- mScheduler.awaitTermination( 100, TimeUnit.MILLISECONDS );
+ /**
+ * Schedule the DFT to run calculations at a fixed rate
+ */
+ int initialDelay = 0;
+ int period = (int)( 1000 / mFrameRate );
+ TimeUnit unit = TimeUnit.MILLISECONDS;
+
+ mScheduler.scheduleAtFixedRate( new DFTCalculationTask(),
+ initialDelay, period, unit );
}
+
+ public void stop()
+ {
+ /**
+ * Shutdown the scheduler and clear out any remaining tasks
+ */
+ try
+ {
+ mScheduler.shutdown();
+ mScheduler.awaitTermination( 100, TimeUnit.MILLISECONDS );
+ }
+ catch ( InterruptedException e )
+ {
+ Log.error( "DFTProcessor - exception while awaiting shutdown of "
+ + "calculation scheduler for reset" );
+ }
+ }
+
+ public void restart()
+ {
+ stop();
+ start();
+ }
+
public int getCalculationsPerSecond()
{
return mFrameRate;
@@ -226,7 +222,10 @@ public class DFTProcessor implements Listener,
{
if( !mQueue.offer( samples ) )
{
- Log.error( "DFTProcessor - queue is full, purging queue, samples[" + samples + "]" );
+ Log.error( "DFTProcessor - [" + mSampleType.toString()
+ + "]queue is full, purging queue, "
+ + "samples[" + samples + "]" );
+
mQueue.clear();
mQueue.offer( samples );
}
@@ -236,8 +235,6 @@ public class DFTProcessor implements Listener,
{
mCurrentBuffer = null;
- long start = System.currentTimeMillis();
-
try
{
mCurrentBuffer = mQueue.take();
@@ -252,107 +249,66 @@ public class DFTProcessor implements Listener,
private float[] getSamples()
{
- int remaining;
-
- if( mSampleType == SampleType.COMPLEX )
- {
- remaining = mFFTWidth.getWidth() * 2;
- }
- else
- {
- remaining = mFFTWidth.getWidth();
- }
+ int remaining = (int)mFFTFloatsPerFrame;
+
+ float[] currentFrame = new float[ remaining ];
- float[] currentFrame = new float[ mFFTWidth.getWidth() * 2 ];
int currentFramePointer = 0;
-
- /* Get the integer component of the overlap, leaving the fractional
- * residual for the next frame */
- float requiredOverlap = mOverlapPerFrame + mOverlapResidual;
- int overlap = (int)requiredOverlap;
- mOverlapResidual = requiredOverlap - overlap;
- /* If positive overlap, we have to re-use samples from previous frame */
- if( overlap > 0 )
+ float integralFloatsToConsume = mNewFloatsPerFrame + mNewFloatResidual;
+
+ int newFloatsToConsumeThisFrame = (int)integralFloatsToConsume;
+
+ mNewFloatResidual = integralFloatsToConsume - newFloatsToConsumeThisFrame;
+
+ /* If the number of required floats for the fft is greater than the
+ * consumption rate per frame, we have to reach into the previous
+ * frame to makeup the difference. */
+ if( newFloatsToConsumeThisFrame < remaining )
{
- int previousFramePointer;
- int length;
+ int previousFloatsRequired = remaining - newFloatsToConsumeThisFrame;
- if( mSampleType == SampleType.COMPLEX )
- {
- length = overlap * 2;
- previousFramePointer = mPreviousFrame.length - length;
- }
- else
- {
- length = overlap;
- previousFramePointer = (int)(mPreviousFrame.length / 2) - length;
- }
+ System.arraycopy( mPreviousFrame,
+ mPreviousFrame.length - previousFloatsRequired,
+ currentFrame,
+ currentFramePointer,
+ previousFloatsRequired );
+
+ remaining -= previousFloatsRequired;
+ currentFramePointer += previousFloatsRequired;
+ }
- System.arraycopy( mPreviousFrame, previousFramePointer,
- currentFrame, currentFramePointer, length );
-
- remaining -= length;
- currentFramePointer += length;
- }
-
- /* If negative overlap, we have to throw away samples */
- else
- {
- if( mSampleType == SampleType.COMPLEX )
- {
- purge( -( overlap * 2 ) );
- }
- else
- {
- purge( -overlap );
- }
- }
-
+ /* Fill the rest of the buffer with new samples */
while( mRunning.get() && remaining > 0 )
{
if( mCurrentBuffer == null ||
- mCurrentBufferPointer == mCurrentBuffer.length )
+ mCurrentBufferPointer >= mCurrentBuffer.length )
{
getNextBuffer();
}
int samplesAvailable = mCurrentBuffer.length - mCurrentBufferPointer;
- if( samplesAvailable >= remaining )
+ while( remaining > 0 && samplesAvailable > 0 )
{
- while( remaining > 0 )
- {
- currentFrame[ currentFramePointer ] =
- (float)mCurrentBuffer[ mCurrentBufferPointer ];
-
- currentFramePointer++;
- mCurrentBufferPointer++;
- remaining --;
- }
-
- mCurrentBufferPointer += remaining;
- remaining = 0;
- }
- else
- {
- while( samplesAvailable > 0 )
- {
- currentFrame[ currentFramePointer ] =
- (float)mCurrentBuffer[ mCurrentBufferPointer ];
+ currentFrame[ currentFramePointer++ ] =
+ (float)mCurrentBuffer[ mCurrentBufferPointer++ ];
- currentFramePointer++;
- mCurrentBufferPointer++;
- samplesAvailable--;
- remaining--;
- }
+ samplesAvailable--;
+ remaining--;
+ newFloatsToConsumeThisFrame--;
}
}
-
- /* If frames are overlapping, store current frame to use next time */
- if( mOverlapPerFrame > 0 )
+
+ /* If the incoming float rate is greater than the fft consumption rate,
+ * then we have to purge some floats, otherwise, store the previous
+ * frame, because we have overlapping frames */
+ if( newFloatsToConsumeThisFrame > 0 )
+ {
+ purge( newFloatsToConsumeThisFrame );
+ }
+ else
{
- mPreviousFrame = null;
mPreviousFrame = Arrays.copyOf( currentFrame, currentFrame.length );
}
@@ -367,7 +323,7 @@ public class DFTProcessor implements Listener,
if( mSampleType == SampleType.FLOAT )
{
- mFFT.realForwardFull( samples );
+ mFFT.realForward( samples );
}
else
{
@@ -389,7 +345,7 @@ public class DFTProcessor implements Listener,
while( mRunning.get() && samplesToPurge > 0 )
{
if( mCurrentBuffer == null ||
- mCurrentBufferPointer == mCurrentBuffer.length )
+ mCurrentBufferPointer >= mCurrentBuffer.length )
{
getNextBuffer();
}
@@ -415,56 +371,20 @@ public class DFTProcessor implements Listener,
*/
private void dispatch( float[] results )
{
- float dc = Math.abs( results[ 0 ] );
+ Iterator it = mListeners.iterator();
- /* Ensure we have power in the DC power bin (bin 0), otherwise don't
- * dispatch the results */
- if( dc != 0 )
+ while( it.hasNext() )
{
- float[] processed = new float[ results.length / 2 ];
-
- int half = results.length / 2;
- int quarter = results.length / 4;
-
- for( int x = 2; x < half; x += 2 )
- {
- processed[ quarter + ( x / 2 ) ] =
- (float)( Math.log10(
- (double)( Math.abs( results[ x ] ) /
- dc ) ) );
- }
-
- for( int x = 0; x < quarter - 1; x++ )
- {
- processed[ x ] =
- (float)( Math.log10(
- (double)( Math.abs( results[ half + ( ( x + 1 ) * 2 ) ] ) /
- dc ) ) );
- }
-
- //Real bin n/2 -1
- processed[ quarter - 1 ] = (float)( Math.log10(
- (double)( Math.abs( results[ 1 ] ) / dc ) ) );
-
- Iterator it = mListeners.iterator();
-
- while( it.hasNext() )
- {
- it.next().receive( processed );
- }
+ it.next().receive( results );
}
-
- results = null;
}
- @Override
- public void addListener( DFTResultsListener listener )
+ public void addConverter( DFTResultsConverter listener )
{
mListeners.add( listener );
}
- @Override
- public void removeListener( DFTResultsListener listener )
+ public void removeConverter( DFTResultsConverter listener )
{
mListeners.remove( listener );
}
@@ -483,11 +403,6 @@ public class DFTProcessor implements Listener,
mRunning.set( false );
}
- else
- {
- Log.error( "DFTProcessor - calculation already in progress"
- + " - skipping this iteration" );
- }
}
}
@@ -502,12 +417,18 @@ public class DFTProcessor implements Listener,
{
mFFTWidth = mNewFFTWidth;
- calculateOverlap();
+ calculateConsumptionRate();
- mWindow = Window.getWindow( WindowType.HANNING,
- ( mFFTWidth.getWidth() * 2 ) );
-
- mPreviousFrame = new float[ mFFTWidth.getWidth() * 2 ];
+ setWindowType( mWindowType );
+
+ if( mSampleType == SampleType.COMPLEX )
+ {
+ mPreviousFrame = new float[ mFFTWidth.getWidth() * 2 ];
+ }
+ else
+ {
+ mPreviousFrame = new float[ mFFTWidth.getWidth() ];
+ }
mFFT = new FloatFFT_1D( mFFTWidth.getWidth() );
}
@@ -523,24 +444,30 @@ public class DFTProcessor implements Listener,
{
mSampleRate = bandwidth;
- calculateOverlap();
+ calculateConsumptionRate();
}
/**
- * Positive overlap means we have to re-use samples from the previous
- * frame
*
- * Negative overlap means we have to throw away samples
*/
- private void calculateOverlap()
+ private void calculateConsumptionRate()
{
- mOverlapPerFrame = 0.0f;
- mOverlapResidual = 0.0f;
+ mNewFloatResidual = 0.0f;
- int requiredSampleRate = mFFTWidth.getWidth() * mFrameRate;
+ mNewFloatsPerFrame = ( (float)mSampleRate / (float)mFrameRate ) *
+ ( mSampleType == SampleType.COMPLEX ? 2.0f : 1.0f );
- int overlap = requiredSampleRate - mSampleRate;
+ mFFTFloatsPerFrame = ( mSampleType == SampleType.COMPLEX ?
+ mFFTWidth.getWidth() * 2 :
+ mFFTWidth.getWidth() );
- mOverlapPerFrame = (float)overlap / (float)mFrameRate;
+ if( mFFTFloatsPerFrame < mNewFloatsPerFrame )
+ {
+ mNewFloatsPerFrameToConsume = mFFTFloatsPerFrame;
+ }
+ else
+ {
+ mNewFloatsPerFrameToConsume = mNewFloatsPerFrame;
+ }
}
}
diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java
index 1abad4ab..d58c993b 100644
--- a/src/spectrum/OverlayPanel.java
+++ b/src/spectrum/OverlayPanel.java
@@ -40,12 +40,12 @@ import source.tuner.FrequencyChangeListener;
import source.tuner.TunerChannel;
import controller.ResourceManager;
import controller.channel.Channel;
-import controller.channel.Channel.ChannelEvent;
import controller.channel.Channel.ChannelType;
-import controller.channel.ChannelListener;
+import controller.channel.ChannelEvent;
+import controller.channel.ChannelEventListener;
public class OverlayPanel extends JPanel
- implements ChannelListener,
+ implements ChannelEventListener,
FrequencyChangeListener,
SettingChangeListener
{
@@ -362,7 +362,7 @@ public class OverlayPanel extends JPanel
for( Channel channel: mVisibleChannels )
{
//Choose the correct background color to use
- if( channel.getSelected() )
+ if( channel.isSelected() )
{
graphics.setColor( mColorChannelConfigSelected );
}
@@ -510,9 +510,11 @@ public class OverlayPanel extends JPanel
*/
@Override
@SuppressWarnings( "incomplete-switch" )
- public void occurred( Channel channel, ChannelEvent component )
+ public void channelChanged( ChannelEvent event )
{
- switch( component )
+ Channel channel = event.getChannel();
+
+ switch( event.getEvent() )
{
case CHANNEL_ADDED:
if( !mChannels.contains( channel ) )
diff --git a/src/spectrum/SpectralDisplayAdjuster.java b/src/spectrum/SpectralDisplayAdjuster.java
new file mode 100644
index 00000000..a627b966
--- /dev/null
+++ b/src/spectrum/SpectralDisplayAdjuster.java
@@ -0,0 +1,55 @@
+package spectrum;
+
+public interface SpectralDisplayAdjuster
+{
+ /**
+ * Gets the amplification multiplier that is applied against the FFT bin
+ * results when displaying the value in the spectral display.
+ *
+ * @return amplification value ( 1 - 100 )
+ */
+ public int getAmplification();
+
+ /**
+ * Sets the amplification multiplier that is applied against the FFT bin
+ * results when displaying the value in the spectral display.
+ *
+ * @param amplification value ( 1 - 100 )
+ */
+ public void setAmplification( int amplification );
+
+ /**
+ * Gets the averaging value indicating the number of FFT results that are
+ * averaged to produce each FFT results output.
+ *
+ * @return averaging value ( 2 - 50 )
+ */
+ public int getAveraging();
+
+ /**
+ * Sets the averaging value indicating the number of FFT results to average
+ * when producing an FFT output result.
+ * @param averaging value ( 2 - 50 )
+ */
+ public void setAveraging( int averaging );
+
+ /**
+ * Gets the baseline used when plotting the FFT bin results in the FFT
+ * spectral display. This value determines the vertical position within the
+ * spectral display upon which the amplified values are added and then
+ * plotted.
+ *
+ * @return averaging value ( 1 - 50 )
+ */
+ public int getBaseline();
+
+ /**
+ * Sets the baseline used when plotting the FFT bin results in the FFT
+ * spectral display. This value determines the vertical position within the
+ * spectral display upon which the amplified values are added and then
+ * plotted.
+ *
+ * @param baseline value ( 1 - 50 )
+ */
+ public void setBaseline( int baseline );
+}
diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java
index 437a337c..1987a3c9 100644
--- a/src/spectrum/SpectralDisplayPanel.java
+++ b/src/spectrum/SpectralDisplayPanel.java
@@ -29,6 +29,7 @@ import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.text.DecimalFormat;
+import java.text.Format;
import java.util.ArrayList;
import javax.swing.JCheckBoxMenuItem;
@@ -55,13 +56,21 @@ import source.SourceException;
import source.tuner.FrequencyChangeListener;
import source.tuner.Tuner;
import source.tuner.TunerSelectionListener;
+import spectrum.converter.ComplexDecibelConverter;
+import spectrum.converter.DFTResultsConverter;
+import spectrum.menu.AmplificationItem;
+import spectrum.menu.AveragingItem;
+import spectrum.menu.BaselineItem;
+import spectrum.menu.FFTWidthItem;
+import spectrum.menu.FFTWindowTypeItem;
+import spectrum.menu.FrameRateItem;
import com.jidesoft.swing.JideSplitPane;
import controller.ResourceManager;
import controller.channel.Channel;
-import controller.channel.Channel.ChannelEvent;
-import controller.channel.ChannelListener;
+import controller.channel.ChannelEvent;
+import controller.channel.ChannelEventListener;
import decode.DecoderType;
import dsp.filter.Window.WindowType;
@@ -74,7 +83,7 @@ import dsp.filter.Window.WindowType;
* in the spectral and waterfall displays.
*/
public class SpectralDisplayPanel extends JPanel
- implements ChannelListener,
+ implements ChannelEventListener,
Listener,
FrequencyChangeListener,
SettingChangeListener,
@@ -98,8 +107,9 @@ public class SpectralDisplayPanel extends JPanel
private JLayeredPane mLayeredPanel;
private SpectrumPanel mSpectrumPanel;
private WaterfallPanel mWaterfallPanel;
- private OverlayPanel mChannelPanel;
+ private OverlayPanel mOverlayPanel;
private DFTProcessor mDFTProcessor;
+ private DFTResultsConverter mDFTConverter;
private ResourceManager mResourceManager;
private Tuner mTuner;
@@ -122,14 +132,17 @@ public class SpectralDisplayPanel extends JPanel
mDFTProcessor.dispose();
mDFTProcessor = null;
+ mDFTConverter.dispose();
+ mDFTConverter = null;
+
mSpectrumPanel.dispose();
mSpectrumPanel = null;
mWaterfallPanel.dispose();
mWaterfallPanel = null;
- mChannelPanel.dispose();
- mChannelPanel = null;
+ mOverlayPanel.dispose();
+ mOverlayPanel = null;
mResourceManager = null;
@@ -193,9 +206,9 @@ public class SpectralDisplayPanel extends JPanel
@Override
public void settingChanged( Setting setting )
{
- if( mChannelPanel != null )
+ if( mOverlayPanel != null )
{
- mChannelPanel.settingChanged( setting );
+ mOverlayPanel.settingChanged( setting );
}
if( mWaterfallPanel != null )
@@ -274,14 +287,14 @@ public class SpectralDisplayPanel extends JPanel
//Create the spectrum panel
mSpectrumPanel = new SpectrumPanel( mResourceManager );
- //Create the channel panel
- mChannelPanel = new OverlayPanel( mResourceManager );
- mChannelPanel.addMouseListener( mouser );
- mChannelPanel.addMouseMotionListener( mouser );
+ //Create the overlay panel
+ mOverlayPanel = new OverlayPanel( mResourceManager );
+ mOverlayPanel.addMouseListener( mouser );
+ mOverlayPanel.addMouseMotionListener( mouser );
//Add the spectrum and channel panels to the layered panel
mLayeredPanel.add( mSpectrumPanel, new Integer( 0 ), 0 );
- mLayeredPanel.add( mChannelPanel, new Integer( 1 ), 0 );
+ mLayeredPanel.add( mOverlayPanel, new Integer( 1 ), 0 );
//Create the waterfall
mWaterfallPanel = new WaterfallPanel( mResourceManager );
@@ -311,8 +324,11 @@ public class SpectralDisplayPanel extends JPanel
* spectrum panel to receive the processed dft results
*/
mDFTProcessor = new DFTProcessor( SampleType.COMPLEX );
- mDFTProcessor.addListener( (DFTResultsListener)mSpectrumPanel );
- mDFTProcessor.addListener( (DFTResultsListener)mWaterfallPanel );
+ mDFTConverter = new ComplexDecibelConverter();
+ mDFTProcessor.addConverter( mDFTConverter );
+
+ mDFTConverter.addListener( (DFTResultsListener)mSpectrumPanel );
+ mDFTConverter.addListener( (DFTResultsListener)mWaterfallPanel );
}
/**
@@ -321,17 +337,17 @@ public class SpectralDisplayPanel extends JPanel
@Override
public void frequencyChanged( long frequency, int bandwidth )
{
- mChannelPanel.frequencyChanged( frequency, bandwidth );
+ mOverlayPanel.frequencyChanged( frequency, bandwidth );
mDFTProcessor.frequencyChanged( frequency, bandwidth );
}
/**
- * Channel config change events
+ * Channel change events
*/
@Override
- public void occurred( Channel config, ChannelEvent component )
+ public void channelChanged( ChannelEvent event )
{
- mChannelPanel.occurred( config, component );
+ mOverlayPanel.channelChanged( event );
}
@Override
@@ -402,7 +418,7 @@ public class SpectralDisplayPanel extends JPanel
Component c = e.getComponent();
mSpectrumPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
- mChannelPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
+ mOverlayPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
}
@Override
@@ -432,24 +448,24 @@ public class SpectralDisplayPanel extends JPanel
private void update( MouseEvent event )
{
- if( event.getComponent() == mChannelPanel )
+ if( event.getComponent() == mOverlayPanel )
{
- mChannelPanel.setCursorLocation( event.getPoint() );
+ mOverlayPanel.setCursorLocation( event.getPoint() );
}
else
{
mWaterfallPanel.setCursorLocation( event.getPoint() );
mWaterfallPanel.setCursorFrequency(
- mChannelPanel.getFrequencyFromAxis( event.getPoint().x ) );
+ mOverlayPanel.getFrequencyFromAxis( event.getPoint().x ) );
}
}
@Override
public void mouseEntered( MouseEvent e )
{
- if( e.getComponent() == mChannelPanel )
+ if( e.getComponent() == mOverlayPanel )
{
- mChannelPanel.setCursorVisible( true );
+ mOverlayPanel.setCursorVisible( true );
}
else
{
@@ -460,7 +476,7 @@ public class SpectralDisplayPanel extends JPanel
@Override
public void mouseExited( MouseEvent e )
{
- mChannelPanel.setCursorVisible( false );
+ mOverlayPanel.setCursorVisible( false );
mWaterfallPanel.setCursorVisible( false );
}
@@ -480,12 +496,12 @@ public class SpectralDisplayPanel extends JPanel
}
long frequency =
- mChannelPanel.getFrequencyFromAxis( event.getX() );
+ mOverlayPanel.getFrequencyFromAxis( event.getX() );
- if( event.getComponent() == mChannelPanel )
+ if( event.getComponent() == mOverlayPanel )
{
ArrayList channels =
- mChannelPanel.getChannelsAtFrequency( frequency );
+ mOverlayPanel.getChannelsAtFrequency( frequency );
for( Channel channel: channels )
{
@@ -562,6 +578,44 @@ public class SpectralDisplayPanel extends JPanel
JMenu displayMenu = new JMenu( "Display" );
contextMenu.add( displayMenu );
+ if( event.getComponent() == mWaterfallPanel )
+ {
+ /**
+ * Baseline menu
+ */
+ JMenu baselineMenu = new JMenu( "Baseline" );
+ baselineMenu.add(
+ new BaselineItem( mWaterfallPanel, 50 ) );
+ displayMenu.add( baselineMenu );
+ }
+ else
+ {
+ /**
+ * Amplification menu
+ */
+ JMenu amplificationMenu = new JMenu( "Amplify" );
+ amplificationMenu.add(
+ new AmplificationItem( mSpectrumPanel, 50 ) );
+ displayMenu.add( amplificationMenu );
+
+ /**
+ * Averaging menu
+ */
+ JMenu averagingMenu = new JMenu( "Average" );
+ averagingMenu.add(
+ new AveragingItem( mSpectrumPanel, 4 ) );
+ displayMenu.add( averagingMenu );
+
+ /**
+ * Baseline menu
+ */
+ JMenu baselineMenu = new JMenu( "Baseline" );
+ baselineMenu.add(
+ new BaselineItem( mSpectrumPanel, 50 ) );
+ displayMenu.add( baselineMenu );
+ }
+
+
/**
* FFT width
*/
@@ -602,9 +656,9 @@ public class SpectralDisplayPanel extends JPanel
if( contextMenu != null )
{
- if( event.getComponent() == mChannelPanel )
+ if( event.getComponent() == mOverlayPanel )
{
- contextMenu.show( mChannelPanel,
+ contextMenu.show( mOverlayPanel,
event.getX(),
event.getY() );
}
@@ -658,95 +712,6 @@ public class SpectralDisplayPanel extends JPanel
}
}
- public class FFTWidthItem extends JCheckBoxMenuItem
- {
- private static final long serialVersionUID = 1L;
-
- private DFTProcessor mDFTProcessor;
- private FFTWidth mFFTWidth;
-
- public FFTWidthItem( DFTProcessor processor, FFTWidth width )
- {
- super( width.getLabel() );
-
- mDFTProcessor = processor;
- mFFTWidth = width;
-
- if( processor.getFFTWidth() == mFFTWidth )
- {
- setSelected( true );
- }
-
- addActionListener( new ActionListener()
- {
- @Override
- public void actionPerformed( ActionEvent arg0 )
- {
- mDFTProcessor.setFFTSize( mFFTWidth );
- }
- } );
- }
- }
-
- public class FFTWindowTypeItem extends JCheckBoxMenuItem
- {
- private static final long serialVersionUID = 1L;
-
- private DFTProcessor mDFTProcessor;
- private WindowType mWindowType;
-
- public FFTWindowTypeItem( DFTProcessor processor, WindowType windowType )
- {
- super( windowType.toString() );
-
- mDFTProcessor = processor;
- mWindowType = windowType;
-
- if( processor.getWindowType() == mWindowType )
- {
- setSelected( true );
- }
-
- addActionListener( new ActionListener()
- {
- @Override
- public void actionPerformed( ActionEvent arg0 )
- {
- mDFTProcessor.setWindowType( mWindowType );
- }
- } );
- }
- }
-
- public class FrameRateItem extends JCheckBoxMenuItem
- {
- private static final long serialVersionUID = 1L;
-
- private DFTProcessor mDFTProcessor;
- private int mFrameRate;
-
- public FrameRateItem( DFTProcessor processor, int frameRate )
- {
- super( String.valueOf( frameRate ) );
-
- mDFTProcessor = processor;
- mFrameRate = frameRate;
-
- if( processor.getFrameRate() == mFrameRate )
- {
- setSelected( true );
- }
-
- addActionListener( new ActionListener()
- {
- @Override
- public void actionPerformed( ActionEvent arg0 )
- {
- mDFTProcessor.setFrameRate( mFrameRate );
- }
- } );
- }
- }
/**
* Context menu item to provide one-click access to starting a channel
diff --git a/src/spectrum/SpectrumPanel.java b/src/spectrum/SpectrumPanel.java
index be9035c1..7f30fd1b 100644
--- a/src/spectrum/SpectrumPanel.java
+++ b/src/spectrum/SpectrumPanel.java
@@ -29,33 +29,42 @@ import java.awt.geom.Line2D;
import javax.swing.JPanel;
+import log.Log;
import settings.ColorSetting;
import settings.ColorSetting.ColorSettingName;
+import settings.IntegerSetting;
import settings.Setting;
import settings.SettingChangeListener;
import buffer.FloatArrayCircularAveragingBuffer;
+import buffer.FloatAveragingBuffer;
import controller.ResourceManager;
public class SpectrumPanel extends JPanel
implements DFTResultsListener,
- SettingChangeListener
+ SettingChangeListener,
+ SpectralDisplayAdjuster
{
private static final long serialVersionUID = 1L;
/* Set display bins size to 1, so that we're guaranteed a reset to the
* correct width once the first sample set arrives */
private float[] mDisplayFFTBins = new float[ 1 ];
- private FloatArrayCircularAveragingBuffer mFFTAveragingBuffer =
- new FloatArrayCircularAveragingBuffer( 4 );
+ private FloatArrayCircularAveragingBuffer mFFTAveragingBuffer;
+ private FloatAveragingBuffer mScaleBuffer = new FloatAveragingBuffer( 20 );
+ private int mAmplification = 50;
+ private int mAveraging = 4;
+ private int mBaseline = 50;
/**
* Spectral Display Color Settings
*/
- private static final String sSPECTRUM_BACKGROUND = "spectrum_background";
- private static final String sSPECTRUM_GRADIENT_TOP = "spectrum_gradient_top";
- private static final String sSPECTRUM_GRADIENT_BOTTOM = "spectrum_gradient_bottom";
- private static final String sSPECTRUM_LINE = "spectrum_line";
- private static final int sSPECTRUM_TRANSLUCENCY = 128;
+ private static final String SPECTRUM_BACKGROUND = "spectrum_background";
+ private static final String SPECTRUM_GRADIENT_TOP = "spectrum_gradient_top";
+ private static final String SPECTRUM_GRADIENT_BOTTOM = "spectrum_gradient_bottom";
+ private static final String SPECTRUM_LINE = "spectrum_line";
+ private static final String SPECTRUM_AVERAGING_SIZE = "spectrum_averaging_size";
+ private static final int SPECTRUM_TRANSLUCENCY = 128;
+
private Color mColorSpectrumBackground;
private Color mColorSpectrumGradientTop;
@@ -63,14 +72,16 @@ public class SpectrumPanel extends JPanel
private Color mColorSpectrumLine;
private float mSpectrumInset = 20.0f;
-
+
private ResourceManager mResourceManager;
public SpectrumPanel( ResourceManager resourceManager )
{
mResourceManager = resourceManager;
- setColors();
+ getSettings();
+
+ setAveraging( mAveraging );
}
public void dispose()
@@ -78,7 +89,50 @@ public class SpectrumPanel extends JPanel
mResourceManager = null;
}
- private void setColors()
+ public void setAveraging( int size )
+ {
+ mAveraging = size;
+
+ mResourceManager.getSettingsManager().setIntegerSetting(
+ SPECTRUM_AVERAGING_SIZE, mAveraging );
+
+ mFFTAveragingBuffer =
+ new FloatArrayCircularAveragingBuffer( mAveraging );
+ }
+
+ public int getAveraging()
+ {
+ return mAveraging;
+ }
+
+ public void setAmplification( int amplification )
+ {
+ mAmplification = amplification;
+ }
+
+ public int getAmplification()
+ {
+ return mAmplification;
+ }
+
+ public void setBaseline( int baseline )
+ {
+ mBaseline = baseline;
+ }
+
+ public int getBaseline()
+ {
+ return mBaseline;
+ }
+
+ public void clearSpectrum()
+ {
+ mDisplayFFTBins = null;
+
+ repaint();
+ }
+
+ private void getSettings()
{
mColorSpectrumBackground =
getColor( ColorSettingName.SPECTRUM_BACKGROUND );
@@ -90,6 +144,16 @@ public class SpectrumPanel extends JPanel
getColor( ColorSettingName.SPECTRUM_GRADIENT_TOP );
mColorSpectrumLine = getColor( ColorSettingName.SPECTRUM_LINE );
+
+ mAveraging = getIntegerSetting( "spectrum_averaging_size", 4 );
+ }
+
+ private int getIntegerSetting( String name, int defaultValue )
+ {
+ IntegerSetting setting = mResourceManager.getSettingsManager()
+ .getIntegerSetting( name, defaultValue );
+
+ return setting.getValue();
}
private Color getColor( ColorSettingName name )
@@ -203,25 +267,45 @@ public class SpectrumPanel extends JPanel
//If we have FFT data to display ...
if( mDisplayFFTBins != null )
{
+ int binCount = mDisplayFFTBins.length - 1;
+
+ float amplify = mAmplification / 50.0f;
+
float insideHeight = size.height - mSpectrumInset;
+ float baseline = ( insideHeight * ( 50.0f / (float)mBaseline ) );
+
+ /* Use the DC component as a reference to scale values to panel height */
+ float scale = mScaleBuffer.get( ( insideHeight * 0.75f ) /
+ mDisplayFFTBins[ binCount ] );
+
float insideWidth = size.width;
- float binSize = insideWidth / ( mDisplayFFTBins.length - 1 );
- float heightMultiplier = (float)( insideHeight * .15 );
+ float binSize = insideWidth / ( binCount );
- float spectrumMiddle = insideHeight * .23f;
-
- for( int x = 0; x < mDisplayFFTBins.length; x++ )
+ for( int x = 0; x < binCount; x++ )
{
- float height = spectrumMiddle - ( mDisplayFFTBins[ x ] * heightMultiplier );
+ /**
+ * Height is an inverse value reflecting the drawable spectral
+ * display vertical axis between the inset value at the bottom
+ * and the top of the display. Values range from:
+ *
+ * top = 0
+ * bottom = insideHeight = panel height - mSpectrumInset
+ */
+ float height = baseline - ( mDisplayFFTBins[ x ] * scale * amplify );
- if( height > size.getHeight() - mSpectrumInset )
+ if( height > insideHeight )
{
- height = (float)size.getHeight() - mSpectrumInset;
+ height = insideHeight;
}
- spectrumShape.lineTo( binSize * x, height );
+ if( height < 0 )
+ {
+ height = 0;
+ }
+
+ spectrumShape.lineTo( ( x * binSize ), height );
}
}
//Otherwise show an empty spectrum
diff --git a/src/spectrum/WaterfallPanel.java b/src/spectrum/WaterfallPanel.java
index b19101e6..4fac1214 100644
--- a/src/spectrum/WaterfallPanel.java
+++ b/src/spectrum/WaterfallPanel.java
@@ -47,7 +47,8 @@ import controller.ResourceManager;
*/
public class WaterfallPanel extends JPanel implements DFTResultsListener,
Pausable,
- SettingChangeListener
+ SettingChangeListener,
+ SpectralDisplayAdjuster
{
private static final long serialVersionUID = 1L;
private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" );
@@ -61,11 +62,7 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
private ColorModel mColorModel = WaterfallColorModel.getDefaultColorModel();
private Color mColorSpectrumCursor;
private Image mWaterfallImage;
-
- float mAverage = 0.0f;
- double mAlpha = 0.95d;
- int mScalor = 85;
- int temp = 0;
+ private int mBaseline = 50;
private Point mCursorLocation = new Point( 0, 0 );
private boolean mCursorVisible = false;
@@ -108,7 +105,7 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
mWaterfallImage = createImage( mMemoryImageSource );
}
-
+
/**
* Pausable interface - pauses updates to the screen
*/
@@ -210,9 +207,9 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
public void receive( float[] update )
{
//If our FFT size changes, reset our pixel map and image source
- if( mFFTSize != update.length )
+ if( mFFTSize != update.length - 1 )
{
- mFFTSize = update.length;
+ mFFTSize = update.length - 1;
reset();
}
@@ -221,37 +218,41 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
System.arraycopy( mPixels, 0,
mPixels, mFFTSize, mPixels.length - mFFTSize );
- //Convert the power spectrum bin values into a byte (0-255)
- //value to use with the color mapping
+ float baseline = mBaseline / 50.0f;
- double averagingTotal = 0.0;
+ /**
+ * Scale the power spectrum bin values into a byte range (0-255) value
+ * using the last bin as the scaling factor
+ */
+ float scale = 256.0f / update[ update.length - 1 ];
+
+ double sum = 0.0d;
- for( int x = 0; x < update.length; x++ )
+ for( int x = 0; x < update.length - 1; x++ )
{
- float value = update[ x ];
-
- averagingTotal += value;
-
- //Average the values toward zero
- float pixel = value - mAverage;
-
- //If we're below the average, make the negative value a positive
- if( pixel < 0 )
- {
-// pixel = -pixel;
- pixel = 0.0f;
- }
-
- //Scale the value
- pixel *= mScalor;
-
-// mPixels[ x ] = (byte)pixel;
- mPixels[ x ] = (byte)( mIntraBinAveragingBuffer.get( pixel ) );
+ sum += update[ x ];
}
- //Update the average value
- mAverage = (float) averagingTotal / update.length;
+ float average = (float)( sum / (double)update.length - 1 );
+
+ for( int x = 0; x < update.length - 1; x++ )
+ {
+ float value = ( update[ x ] - average ) * scale * baseline;
+ if( value < 0 )
+ {
+ mPixels[ x ] = 0;
+ }
+ else if( value > 255 )
+ {
+ mPixels[ x ] = (byte)255;
+ }
+ else
+ {
+ mPixels[ x ] = (byte)value;
+ }
+ }
+
if( !mPaused.get() )
{
EventQueue.invokeLater( new Runnable()
@@ -284,4 +285,42 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
@Override
public void settingDeleted( Setting setting ) {}
+
+ @Override
+ public void setBaseline( int baseline )
+ {
+ mBaseline = baseline;
+ }
+
+ @Override
+ public int getBaseline()
+ {
+ return mBaseline;
+ }
+
+ @Override
+ public int getAmplification()
+ {
+ /* Not Implemented */
+ return 0;
+ }
+
+ @Override
+ public void setAmplification( int amplification )
+ {
+ /* Not Implemented */
+ }
+
+ @Override
+ public int getAveraging()
+ {
+ /* Not Implemented */
+ return 0;
+ }
+
+ @Override
+ public void setAveraging( int averaging )
+ {
+ /* Not Implemented */
+ }
}
diff --git a/src/spectrum/converter/ComplexDecibelConverter.java b/src/spectrum/converter/ComplexDecibelConverter.java
new file mode 100644
index 00000000..e88e0480
--- /dev/null
+++ b/src/spectrum/converter/ComplexDecibelConverter.java
@@ -0,0 +1,62 @@
+package spectrum.converter;
+
+public class ComplexDecibelConverter extends DFTResultsConverter
+{
+ /**
+ * Converts the output of the JTransforms FloatFFT_1D.complexForward()
+ * calculation into a normalized power spectrum in decibels, per description
+ * in Lyons, Understanding Digital Signal Processing, 3e, page 141.
+ */
+ @Override
+ public void receive( float[] results )
+ {
+ float dc = Math.abs( results[ 0 ] );
+
+ /* Ensure we have power in the DC power bin (bin 0), otherwise don't
+ * dispatch the results */
+ if( dc > 0 )
+ {
+ float[] processed = new float[ results.length / 2 + 1 ];
+
+ /**
+ * Output from JTransforms 3.0 has the lower and upper halves of the
+ * spectrum swapped.
+ */
+ int half = processed.length / 2;
+
+ for( int x = 0; x < processed.length - 2; x ++ )
+ {
+ int index = ( x + 1 ) * 2;
+
+ double magnitude2 = ( ( results[ index ] * results[ index ] ) +
+ ( results[ index + 1 ] * results[ index + 1 ] ) );
+
+ if( x < half )
+ {
+ processed[ x + half ] = 10.0f *
+ (float)( Math.log10( magnitude2 / dc ) );
+ }
+ else
+ {
+ processed[ x - half ] = 10.0f *
+ (float)( Math.log10( magnitude2 / dc ) );
+ }
+ }
+
+ /**
+ * Place scaled dc component in last bin of processed array as
+ * scaling reference
+ */
+ processed[ results.length / 2 ] = processed[ half ];
+
+ /**
+ * Repair the erroneous DC component bin by using the average of
+ * the neighboring bins
+ */
+ processed[ half ] = ( processed[ half - 1 ] +
+ processed[ half + 1 ] / 2.0f );
+
+ dispatch( processed );
+ }
+ }
+}
diff --git a/src/spectrum/converter/DFTResultsConverter.java b/src/spectrum/converter/DFTResultsConverter.java
new file mode 100644
index 00000000..82eada92
--- /dev/null
+++ b/src/spectrum/converter/DFTResultsConverter.java
@@ -0,0 +1,46 @@
+package spectrum.converter;
+
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import spectrum.DFTResultsListener;
+import spectrum.DFTResultsProvider;
+
+public abstract class DFTResultsConverter
+ implements DFTResultsListener, DFTResultsProvider
+{
+ private CopyOnWriteArrayList mListeners =
+ new CopyOnWriteArrayList();
+
+ /**
+ * DFT Results Converter - for converting the output of the JTransforms
+ * FFT library real and complex forward results
+ */
+ public DFTResultsConverter()
+ {
+ }
+
+ public void dispose()
+ {
+ mListeners.clear();
+ }
+
+ @Override
+ public void addListener( DFTResultsListener listener )
+ {
+ mListeners.add( listener );
+ }
+
+ @Override
+ public void removeListener( DFTResultsListener listener )
+ {
+ mListeners.remove( listener );
+ }
+
+ protected void dispatch( float[] results )
+ {
+ for( DFTResultsListener listener: mListeners )
+ {
+ listener.receive( results );
+ }
+ }
+}
diff --git a/src/spectrum/converter/RealDecibelConverter.java b/src/spectrum/converter/RealDecibelConverter.java
new file mode 100644
index 00000000..69fc9762
--- /dev/null
+++ b/src/spectrum/converter/RealDecibelConverter.java
@@ -0,0 +1,38 @@
+package spectrum.converter;
+
+public class RealDecibelConverter extends DFTResultsConverter
+{
+ /**
+ * Converts the output of the JTransforms FloatFFT_1D.realForward()
+ * calculation into a normalized power spectrum in decibels, per description
+ * in Lyons, Understanding Digital Signal Processing, 3e, page 141.
+ */
+ @Override
+ public void receive( float[] results )
+ {
+ float dc = Math.abs( results[ 0 ] );
+
+ /* Ensure we have power in the DC power bin (bin 0), otherwise don't
+ * dispatch the results */
+ if( dc != 0 )
+ {
+ /* Note: we're only processing 1/4 of the output samples */
+ float[] processed = new float[ results.length / 4 + 1 ];
+
+ for( int x = 0; x < processed.length - 2; x ++ )
+ {
+ int index = ( x + 1 ) * 2;
+
+ double magnitude = ( ( results[ index ] * results[ index ] ) +
+ ( results[ index + 1 ] * results[ index + 1 ] ) );
+
+ processed[ x ] = 10.0f *
+ (float)( Math.log10( magnitude / dc ) );
+ }
+
+ processed[ processed.length - 1 ] = processed[ 0 ];
+
+ dispatch( processed );
+ }
+ }
+}
diff --git a/src/spectrum/menu/AmplificationItem.java b/src/spectrum/menu/AmplificationItem.java
new file mode 100644
index 00000000..13bd307a
--- /dev/null
+++ b/src/spectrum/menu/AmplificationItem.java
@@ -0,0 +1,57 @@
+package spectrum.menu;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.JSlider;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import spectrum.SpectralDisplayAdjuster;
+
+public class AmplificationItem extends JSlider implements ChangeListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private SpectralDisplayAdjuster mAdjuster;
+ private int mDefaultValue;
+
+ public AmplificationItem( SpectralDisplayAdjuster adjuster, int defaultValue )
+ {
+ super( JSlider.HORIZONTAL, 1, 100, adjuster.getAmplification() );
+ mDefaultValue = defaultValue;
+
+ mAdjuster = adjuster;
+
+ setMajorTickSpacing( 10 );
+ setMinorTickSpacing( 5 );
+ setPaintTicks( true );
+
+ addChangeListener( this );
+
+ addMouseListener( new MouseListener()
+ {
+ @Override
+ public void mouseClicked( MouseEvent event )
+ {
+ if( event.getClickCount() == 2 )
+ {
+ AmplificationItem.this.setValue( mDefaultValue );
+ }
+ }
+
+ public void mouseReleased( MouseEvent arg0 ) {}
+ public void mousePressed( MouseEvent arg0 ) {}
+ public void mouseExited( MouseEvent arg0 ) {}
+ public void mouseEntered( MouseEvent arg0 ) {}
+ } );
+ }
+
+ @Override
+ public void stateChanged( ChangeEvent event )
+ {
+ int value = ((JSlider)event.getSource()).getValue();
+
+ mAdjuster.setAmplification( value );
+ }
+}
diff --git a/src/spectrum/menu/AveragingItem.java b/src/spectrum/menu/AveragingItem.java
new file mode 100644
index 00000000..a3a531e4
--- /dev/null
+++ b/src/spectrum/menu/AveragingItem.java
@@ -0,0 +1,57 @@
+package spectrum.menu;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.JSlider;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import spectrum.SpectralDisplayAdjuster;
+
+public class AveragingItem extends JSlider implements ChangeListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private SpectralDisplayAdjuster mAdjuster;
+ private int mDefaultValue;
+
+ public AveragingItem( SpectralDisplayAdjuster adjuster, int defaultValue )
+ {
+ super( JSlider.HORIZONTAL, 2, 50, adjuster.getAveraging() );
+ mDefaultValue = defaultValue;
+
+ mAdjuster = adjuster;
+
+ setMajorTickSpacing( 10 );
+ setMinorTickSpacing( 5 );
+ setPaintTicks( true );
+
+ addChangeListener( this );
+
+ addMouseListener( new MouseListener()
+ {
+ @Override
+ public void mouseClicked( MouseEvent event )
+ {
+ if( event.getClickCount() == 2 )
+ {
+ AveragingItem.this.setValue( mDefaultValue );
+ }
+ }
+
+ public void mouseReleased( MouseEvent arg0 ) {}
+ public void mousePressed( MouseEvent arg0 ) {}
+ public void mouseExited( MouseEvent arg0 ) {}
+ public void mouseEntered( MouseEvent arg0 ) {}
+ } );
+ }
+
+ @Override
+ public void stateChanged( ChangeEvent event )
+ {
+ int value = ((JSlider)event.getSource()).getValue();
+
+ mAdjuster.setAveraging( value );
+ }
+}
diff --git a/src/spectrum/menu/BaselineItem.java b/src/spectrum/menu/BaselineItem.java
new file mode 100644
index 00000000..bfc7bcb6
--- /dev/null
+++ b/src/spectrum/menu/BaselineItem.java
@@ -0,0 +1,57 @@
+package spectrum.menu;
+
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+
+import javax.swing.JSlider;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import spectrum.SpectralDisplayAdjuster;
+
+public class BaselineItem extends JSlider implements ChangeListener
+{
+ private static final long serialVersionUID = 1L;
+
+ private SpectralDisplayAdjuster mAdjuster;
+ private int mDefaultValue;
+
+ public BaselineItem( SpectralDisplayAdjuster adjuster, int defaultValue )
+ {
+ super( JSlider.HORIZONTAL, 1, 100, adjuster.getBaseline() );
+ mDefaultValue = defaultValue;
+
+ mAdjuster = adjuster;
+
+ setMajorTickSpacing( 10 );
+ setMinorTickSpacing( 5 );
+ setPaintTicks( true );
+
+ addChangeListener( this );
+
+ addMouseListener( new MouseListener()
+ {
+ @Override
+ public void mouseClicked( MouseEvent event )
+ {
+ if( event.getClickCount() == 2 )
+ {
+ BaselineItem.this.setValue( mDefaultValue );
+ }
+ }
+
+ public void mouseReleased( MouseEvent arg0 ) {}
+ public void mousePressed( MouseEvent arg0 ) {}
+ public void mouseExited( MouseEvent arg0 ) {}
+ public void mouseEntered( MouseEvent arg0 ) {}
+ } );
+ }
+
+ @Override
+ public void stateChanged( ChangeEvent event )
+ {
+ int value = ((JSlider)event.getSource()).getValue();
+
+ mAdjuster.setBaseline( value );
+ }
+}
diff --git a/src/spectrum/menu/FFTWidthItem.java b/src/spectrum/menu/FFTWidthItem.java
new file mode 100644
index 00000000..4185b33d
--- /dev/null
+++ b/src/spectrum/menu/FFTWidthItem.java
@@ -0,0 +1,39 @@
+package spectrum.menu;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JCheckBoxMenuItem;
+
+import spectrum.DFTProcessor;
+import spectrum.FFTWidth;
+
+public class FFTWidthItem extends JCheckBoxMenuItem
+{
+ private static final long serialVersionUID = 1L;
+
+ private DFTProcessor mDFTProcessor;
+ private FFTWidth mFFTWidth;
+
+ public FFTWidthItem( DFTProcessor processor, FFTWidth width )
+ {
+ super( width.getLabel() );
+
+ mDFTProcessor = processor;
+ mFFTWidth = width;
+
+ if( processor.getFFTWidth() == mFFTWidth )
+ {
+ setSelected( true );
+ }
+
+ addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent arg0 )
+ {
+ mDFTProcessor.setFFTSize( mFFTWidth );
+ }
+ } );
+ }
+}
diff --git a/src/spectrum/menu/FFTWindowTypeItem.java b/src/spectrum/menu/FFTWindowTypeItem.java
new file mode 100644
index 00000000..6a2aacbf
--- /dev/null
+++ b/src/spectrum/menu/FFTWindowTypeItem.java
@@ -0,0 +1,40 @@
+package spectrum.menu;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JCheckBoxMenuItem;
+
+import spectrum.DFTProcessor;
+import dsp.filter.Window.WindowType;
+
+public class FFTWindowTypeItem extends JCheckBoxMenuItem
+{
+ private static final long serialVersionUID = 1L;
+
+ private DFTProcessor mDFTProcessor;
+ private WindowType mWindowType;
+
+ public FFTWindowTypeItem( DFTProcessor processor, WindowType windowType )
+ {
+ super( windowType.toString() );
+
+ mDFTProcessor = processor;
+ mWindowType = windowType;
+
+ if( processor.getWindowType() == mWindowType )
+ {
+ setSelected( true );
+ }
+
+ addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent arg0 )
+ {
+ mDFTProcessor.setWindowType( mWindowType );
+ }
+ } );
+ }
+}
+
diff --git a/src/spectrum/menu/FrameRateItem.java b/src/spectrum/menu/FrameRateItem.java
new file mode 100644
index 00000000..3101e6d4
--- /dev/null
+++ b/src/spectrum/menu/FrameRateItem.java
@@ -0,0 +1,39 @@
+package spectrum.menu;
+
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JCheckBoxMenuItem;
+
+import spectrum.DFTProcessor;
+
+public class FrameRateItem extends JCheckBoxMenuItem
+{
+ private static final long serialVersionUID = 1L;
+
+ private DFTProcessor mDFTProcessor;
+ private int mFrameRate;
+
+ public FrameRateItem( DFTProcessor processor, int frameRate )
+ {
+ super( String.valueOf( frameRate ) );
+
+ mDFTProcessor = processor;
+ mFrameRate = frameRate;
+
+ if( processor.getFrameRate() == mFrameRate )
+ {
+ setSelected( true );
+ }
+
+ addActionListener( new ActionListener()
+ {
+ @Override
+ public void actionPerformed( ActionEvent arg0 )
+ {
+ mDFTProcessor.setFrameRate( mFrameRate );
+ }
+ } );
+ }
+}
+