mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Channel Spectral Display
- Adds Channel Spectrum tab to display FFT of the audio output of the currently selected channel - Upgrades to JTransforms 3.0 FFT library - Adds three new context menus to main and channel spectral displays: -- Baseline - affects the vertical rendering baseline of the FFT -- Amplification - affects the vertical height of each FFT bin -- Averaging - affects how many FFTs are averaged to produce the display
This commit is contained in:
@@ -5,7 +5,6 @@
|
||||
<classpathentry kind="lib" path="imports/commons-lang3-3.2.1.jar"/>
|
||||
<classpathentry kind="lib" path="imports/jide-oss-3.5.15.jar"/>
|
||||
<classpathentry kind="lib" path="imports/jsr80-1.0.1.jar"/>
|
||||
<classpathentry kind="lib" path="imports/jtransforms-2.4.jar"/>
|
||||
<classpathentry kind="lib" path="imports/libusb4java-1.2.0-linux-arm.jar"/>
|
||||
<classpathentry kind="lib" path="imports/libusb4java-1.2.0-linux-x86_64.jar"/>
|
||||
<classpathentry kind="lib" path="imports/libusb4java-1.2.0-linux-x86.jar"/>
|
||||
@@ -18,5 +17,8 @@
|
||||
<classpathentry kind="lib" path="imports/usb4java-1.2.0.jar"/>
|
||||
<classpathentry kind="lib" path="imports/usb4java-javax-1.2.0.jar"/>
|
||||
<classpathentry kind="lib" path="config"/>
|
||||
<classpathentry kind="lib" path="imports/JTransforms-3.0.jar" sourcepath="/home/denny/.m2/repository/com/github/wendykierp/JTransforms/3.0/JTransforms-3.0-sources.jar"/>
|
||||
<classpathentry kind="lib" path="imports/libusb4java-1.2.0-linux-ppc.jar"/>
|
||||
<classpathentry kind="lib" path="imports/JLargeArrays-1.2.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
BIN
imports/JLargeArrays-1.2.jar
Normal file
BIN
imports/JLargeArrays-1.2.jar
Normal file
Binary file not shown.
BIN
imports/JTransforms-3.0.jar
Normal file
BIN
imports/JTransforms-3.0.jar
Normal file
Binary file not shown.
Binary file not shown.
@@ -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<ChannelListener> mChannelConfigChangeListeners =
|
||||
new ArrayList<ChannelListener>();
|
||||
private ArrayList<ChannelEventListener> mChannelConfigChangeListeners =
|
||||
new ArrayList<ChannelEventListener>();
|
||||
|
||||
private ArrayList<TunerSelectionListener> mTunerSelectionListeners =
|
||||
new ArrayList<TunerSelectionListener>();
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -202,6 +202,7 @@ public abstract class CallEvent
|
||||
PAGE( "Page" ),
|
||||
REGISTER( "Register" ),
|
||||
REGISTER_ESN( "ESN" ),
|
||||
REQUEST( "Request" ),
|
||||
SDM( "SDM" ),
|
||||
STATUS( "Status" ),
|
||||
UNKNOWN( "Unknown" );
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<ChannelListener> mChannelListeners =
|
||||
new CopyOnWriteArrayList<ChannelListener>();
|
||||
private CopyOnWriteArrayList<ChannelEventListener> mChannelListeners =
|
||||
new CopyOnWriteArrayList<ChannelEventListener>();
|
||||
|
||||
private CopyOnWriteArrayList<Listener<Message>> mMessageListeners =
|
||||
new CopyOnWriteArrayList<Listener<Message>>();
|
||||
@@ -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<ChannelListener> listeners )
|
||||
public void addListeners( List<ChannelEventListener> 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,
|
||||
}
|
||||
}
|
||||
|
||||
52
src/controller/channel/ChannelEvent.java
Normal file
52
src/controller/channel/ChannelEvent.java
Normal file
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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<Channel> mChannels = new ArrayList<Channel>();
|
||||
|
||||
private ArrayList<ChannelListener> mChannelListeners =
|
||||
new ArrayList<ChannelListener>();
|
||||
private ArrayList<ChannelEventListener> mChannelListeners =
|
||||
new ArrayList<ChannelEventListener>();
|
||||
private ArrayList<Listener<Message>> mMessageListeners =
|
||||
new ArrayList<Listener<Message>>();
|
||||
|
||||
@@ -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<ChannelListener> getChannelListeners()
|
||||
public ArrayList<ChannelEventListener> getChannelListeners()
|
||||
{
|
||||
return mChannelListeners;
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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 ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
|
||||
30
src/settings/IntegerSetting.java
Normal file
30
src/settings/IntegerSetting.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -84,7 +84,7 @@ public enum TunerClass
|
||||
return mVendorDescription + " " + mDeviceDescription;
|
||||
}
|
||||
|
||||
public TunerType getTunerClass()
|
||||
public TunerType getTunerType()
|
||||
{
|
||||
return mTunerType;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
|
||||
264
src/spectrum/ChannelOverlayPanel.java
Normal file
264
src/spectrum/ChannelOverlayPanel.java
Normal file
@@ -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 <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
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 ) {}
|
||||
}
|
||||
520
src/spectrum/ChannelSpectrumPanel.java
Normal file
520
src/spectrum/ChannelSpectrumPanel.java
Normal file
@@ -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<Float>,
|
||||
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<Float>
|
||||
{
|
||||
private FloatHalfBandFilter mDecimationFilter = new FloatHalfBandFilter(
|
||||
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO, 1.0002 );
|
||||
|
||||
private SampleAssembler mSampleAssembler;
|
||||
|
||||
public DecimatingSampleAssembler( Listener<Float[]> listener )
|
||||
{
|
||||
mSampleAssembler = new SampleAssembler( listener );
|
||||
mDecimationFilter.setListener( mSampleAssembler );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void receive( Float sample )
|
||||
{
|
||||
mDecimationFilter.receive( sample );
|
||||
}
|
||||
}
|
||||
|
||||
public class SampleAssembler implements Listener<Float>
|
||||
{
|
||||
private Listener<Float[]> mListener;
|
||||
|
||||
public SampleAssembler( Listener<Float[]> 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 ];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<Float[]>,
|
||||
DFTResultsProvider,
|
||||
FrequencyChangeListener
|
||||
{
|
||||
private CopyOnWriteArrayList<DFTResultsListener> mListeners =
|
||||
new CopyOnWriteArrayList<DFTResultsListener>();
|
||||
private CopyOnWriteArrayList<DFTResultsConverter> mListeners =
|
||||
new CopyOnWriteArrayList<DFTResultsConverter>();
|
||||
|
||||
private ArrayBlockingQueue<Float[]> mQueue =
|
||||
new ArrayBlockingQueue<Float[]>( 200 );
|
||||
@@ -56,14 +58,16 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
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<Float[]>,
|
||||
|
||||
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<Float[]>,
|
||||
+ "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<Float[]>,
|
||||
{
|
||||
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<Float[]>,
|
||||
{
|
||||
mCurrentBuffer = null;
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
|
||||
try
|
||||
{
|
||||
mCurrentBuffer = mQueue.take();
|
||||
@@ -252,107 +249,66 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
|
||||
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<Float[]>,
|
||||
|
||||
if( mSampleType == SampleType.FLOAT )
|
||||
{
|
||||
mFFT.realForwardFull( samples );
|
||||
mFFT.realForward( samples );
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -389,7 +345,7 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
while( mRunning.get() && samplesToPurge > 0 )
|
||||
{
|
||||
if( mCurrentBuffer == null ||
|
||||
mCurrentBufferPointer == mCurrentBuffer.length )
|
||||
mCurrentBufferPointer >= mCurrentBuffer.length )
|
||||
{
|
||||
getNextBuffer();
|
||||
}
|
||||
@@ -415,56 +371,20 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
*/
|
||||
private void dispatch( float[] results )
|
||||
{
|
||||
float dc = Math.abs( results[ 0 ] );
|
||||
Iterator<DFTResultsConverter> 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<DFTResultsListener> 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<Float[]>,
|
||||
|
||||
mRunning.set( false );
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.error( "DFTProcessor - calculation already in progress"
|
||||
+ " - skipping this iteration" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -502,12 +417,18 @@ public class DFTProcessor implements Listener<Float[]>,
|
||||
{
|
||||
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<Float[]>,
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ) )
|
||||
|
||||
55
src/spectrum/SpectralDisplayAdjuster.java
Normal file
55
src/spectrum/SpectralDisplayAdjuster.java
Normal file
@@ -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 );
|
||||
}
|
||||
@@ -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<Float[]>,
|
||||
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<Channel> 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 */
|
||||
}
|
||||
}
|
||||
|
||||
62
src/spectrum/converter/ComplexDecibelConverter.java
Normal file
62
src/spectrum/converter/ComplexDecibelConverter.java
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
46
src/spectrum/converter/DFTResultsConverter.java
Normal file
46
src/spectrum/converter/DFTResultsConverter.java
Normal file
@@ -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<DFTResultsListener> mListeners =
|
||||
new CopyOnWriteArrayList<DFTResultsListener>();
|
||||
|
||||
/**
|
||||
* 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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
38
src/spectrum/converter/RealDecibelConverter.java
Normal file
38
src/spectrum/converter/RealDecibelConverter.java
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/spectrum/menu/AmplificationItem.java
Normal file
57
src/spectrum/menu/AmplificationItem.java
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
57
src/spectrum/menu/AveragingItem.java
Normal file
57
src/spectrum/menu/AveragingItem.java
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
57
src/spectrum/menu/BaselineItem.java
Normal file
57
src/spectrum/menu/BaselineItem.java
Normal file
@@ -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 );
|
||||
}
|
||||
}
|
||||
39
src/spectrum/menu/FFTWidthItem.java
Normal file
39
src/spectrum/menu/FFTWidthItem.java
Normal file
@@ -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 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
40
src/spectrum/menu/FFTWindowTypeItem.java
Normal file
40
src/spectrum/menu/FFTWindowTypeItem.java
Normal file
@@ -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 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
39
src/spectrum/menu/FrameRateItem.java
Normal file
39
src/spectrum/menu/FrameRateItem.java
Normal file
@@ -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 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user