diff --git a/src/controller/ControllerPanel.java b/src/controller/ControllerPanel.java index 6b627649..cb409dda 100644 --- a/src/controller/ControllerPanel.java +++ b/src/controller/ControllerPanel.java @@ -28,13 +28,15 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import map.MapPanel; -import message.Message; +import map.MapService; import module.decode.event.CallEventPanel; import module.decode.event.MessageActivityPanel; import module.decode.state.ChannelList; import net.miginfocom.swing.MigLayout; -import sample.Listener; +import playlist.PlaylistManager; +import settings.SettingsManager; import spectrum.ChannelSpectrumPanel; +import audio.AudioManager; import audio.AudioPanel; import com.jidesoft.swing.JideSplitPane; @@ -62,13 +64,32 @@ public class ControllerPanel extends JPanel protected JideSplitPane mSystemControlSplitPane; protected JTable mChannelActivityTable = new JTable(); - protected ResourceManager mResourceManager; + private AudioPanel mAudioPanel; + private MapPanel mMapPanel; - public ControllerPanel( ResourceManager resourceManager ) + private ChannelManager mChannelManager; + private ConfigurationControllerModel mController; + protected SettingsManager mSettingsManager; + + public ControllerPanel( AudioManager audioManager, + ConfigurationControllerModel controller, + ChannelManager channelManager, + MapService mapService, + PlaylistManager playlistManager, + SettingsManager settingsManager ) { - mResourceManager = resourceManager; + mChannelManager = channelManager; + mController = controller; + mSettingsManager = settingsManager; + + mAudioPanel = new AudioPanel( mSettingsManager, audioManager ); + + mMapPanel = new MapPanel( mapService, mSettingsManager, mChannelManager ); + + mCallEventPanel = new CallEventPanel( mSettingsManager ); + + mChannelStateList = new ChannelList( playlistManager, mSettingsManager ); - mCallEventPanel = new CallEventPanel( resourceManager.getSettingsManager() ); init(); } @@ -80,8 +101,8 @@ public class ControllerPanel extends JPanel //System Configuration View and Editor mConfigurationEditor = new ConfigurationEditor(); - mSystemControlViewPanel = - new ConfigurationTreePanel( mResourceManager.getController() ); + + mSystemControlViewPanel = new ConfigurationTreePanel( mController ); mSystemControlViewPanel.addTreeSelectionListener( mConfigurationEditor ); mSystemControlSplitPane = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); @@ -89,7 +110,7 @@ public class ControllerPanel extends JPanel mSystemControlSplitPane.add( mSystemControlViewPanel ); mSystemControlSplitPane.add( mConfigurationEditor ); - mChannelSpectrumPanel = new ChannelSpectrumPanel( mResourceManager ); + mChannelSpectrumPanel = new ChannelSpectrumPanel( mSettingsManager ); //Tabbed View - configuration, calls, messages, map mTabbedPane = new JideTabbedPane(); @@ -127,37 +148,23 @@ public class ControllerPanel extends JPanel /** * Add mapping services and map panel to a new tab */ - - /* Add Map Service as message listener to receive all messages */ - mResourceManager.getChannelManager().addListener( - (Listener)mResourceManager.getMapService() ); + mTabbedPane.addTab( "Map", mMapPanel ); - MapPanel mapPanel = new MapPanel( mResourceManager ); - mTabbedPane.addTab( "Map", mapPanel ); - - - /* Channel state list */ - mChannelStateList = new ChannelList( mResourceManager.getPlaylistManager(), - mResourceManager.getSettingsManager() ); - /* 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 ); + mChannelManager.addListener( mCallEventPanel ); + mChannelManager.addListener( mChannelStateList ); + mChannelManager.addListener( mChannelSpectrumPanel ); + mChannelManager.addListener( mMessageActivityPanel ); JScrollPane channelStateListScroll = new JScrollPane(); channelStateListScroll.getViewport().setView( mChannelStateList ); channelStateListScroll.setPreferredSize( new Dimension( 200, 300 ) ); - AudioPanel audioPanel = new AudioPanel( mResourceManager.getSettingsManager(), - mResourceManager.getAudioManager() ); JideSplitPane audioChannelListSplit = new JideSplitPane( JideSplitPane.VERTICAL_SPLIT ); audioChannelListSplit.setDividerSize( 5 ); - audioChannelListSplit.add( audioPanel ); + audioChannelListSplit.add( mAudioPanel ); audioChannelListSplit.add( channelStateListScroll ); JideSplitPane channelSplit = new JideSplitPane( JideSplitPane.HORIZONTAL_SPLIT ); @@ -170,6 +177,6 @@ public class ControllerPanel extends JPanel public ConfigurationControllerModel getController() { - return mResourceManager.getController(); + return mController; } } diff --git a/src/gui/SDRTrunk.java b/src/gui/SDRTrunk.java index afd740db..8854c9de 100644 --- a/src/gui/SDRTrunk.java +++ b/src/gui/SDRTrunk.java @@ -38,30 +38,34 @@ import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; -import javax.swing.JScrollPane; import javax.swing.JSeparator; +import map.MapService; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.PlaylistManager; import properties.SystemProperties; +import settings.SettingsManager; import source.tuner.Tuner; import source.tuner.TunerSelectionListener; import spectrum.SpectralDisplayPanel; import util.TimeStamp; +import audio.AudioManager; import com.jidesoft.swing.JideSplitPane; +import controller.ConfigurationControllerModel; import controller.ControllerPanel; import controller.ResourceManager; +import controller.channel.ChannelManager; public class SDRTrunk { private final static Logger mLog = LoggerFactory.getLogger( SDRTrunk.class ); - private ResourceManager mResourceManager; private ControllerPanel mControllerPanel; private SpectralDisplayPanel mSpectralPanel; private JFrame mMainGui = new JFrame(); @@ -73,8 +77,8 @@ public class SDRTrunk mLog.info( "" ); mLog.info( "" ); mLog.info( "*******************************************************************" ); - mLog.info( "**** SDRTrunk: a trunked radio and digital decoding application ***" ); - mLog.info( "**** website: https://code.google.com/p/sdrtrunk ***" ); + mLog.info( "**** sdrtrunk: a trunked radio and digital decoding application ***" ); + mLog.info( "**** website: https://github.com/dsheirer/sdrtrunk ***" ); mLog.info( "*******************************************************************" ); mLog.info( "" ); mLog.info( "" ); @@ -97,8 +101,24 @@ public class SDRTrunk * Construct the resource manager now, so that it can use the system * properties that were just loaded */ - mResourceManager = new ResourceManager(); + ResourceManager resource = new ResourceManager(); + //TODO: get rid of resource manager ... below is start of breaking out + //the encapsulated pieces from the resource manager + + AudioManager audio = resource.getAudioManager(); + ChannelManager channel = resource.getChannelManager(); + ConfigurationControllerModel controller = resource.getController(); + MapService map = resource.getMapService(); + PlaylistManager playlist = resource.getPlaylistManager(); + SettingsManager settings = resource.getSettingsManager(); + + mControllerPanel = new ControllerPanel( audio, controller, channel, + map, playlist, settings ); + + mSpectralPanel = new SpectralDisplayPanel( channel, controller, + playlist, settings ); + mTitle = getTitle(); //Initialize the GUI @@ -140,17 +160,6 @@ public class SDRTrunk "[grow,fill]", "[grow,fill]") ); - mControllerPanel = new ControllerPanel( mResourceManager ); - - mSpectralPanel = new SpectralDisplayPanel( mResourceManager ); - - //Register spectral panel to receive tuner selection events - mControllerPanel.getController() - .addListener( (TunerSelectionListener)mSpectralPanel ); - - //Add spectrum panel to receive channel change events - mResourceManager.getChannelManager().addListener( mSpectralPanel ); - //init() the controller to load tuners and playlists mControllerPanel.getController().init(); @@ -382,7 +391,7 @@ public class SDRTrunk { StringBuilder sb = new StringBuilder(); - sb.append( "SDRTrunk" ); + sb.append( "sdrtrunk" ); try( BufferedReader reader = new BufferedReader( new InputStreamReader( this.getClass() diff --git a/src/map/MapPanel.java b/src/map/MapPanel.java index a5a29ec7..9a0a1e5c 100644 --- a/src/map/MapPanel.java +++ b/src/map/MapPanel.java @@ -21,6 +21,7 @@ import java.awt.EventQueue; import javax.swing.JPanel; +import message.Message; import net.miginfocom.swing.MigLayout; import org.jdesktop.swingx.JXMapViewer; @@ -31,9 +32,10 @@ import org.jdesktop.swingx.mapviewer.DefaultTileFactory; import org.jdesktop.swingx.mapviewer.GeoPosition; import org.jdesktop.swingx.mapviewer.TileFactoryInfo; +import sample.Listener; import settings.MapViewSetting; import settings.SettingsManager; -import controller.ResourceManager; +import controller.channel.ChannelManager; public class MapPanel extends JPanel implements PlottableUpdateListener { @@ -44,11 +46,17 @@ public class MapPanel extends JPanel implements PlottableUpdateListener private JXMapViewer mMapViewer = new JXMapViewer(); private PlottableEntityPainter mMapPainter; - public MapPanel( ResourceManager resourceManager ) + public MapPanel( MapService mapService, + SettingsManager settingsManager, + ChannelManager channelManager ) { - mSettingsManager = resourceManager.getSettingsManager(); - mMapService = resourceManager.getMapService(); + mSettingsManager = settingsManager; + mMapService = mapService; mMapPainter = new PlottableEntityPainter( mSettingsManager ); + + /* Add Map Service as message listener to receive all messages */ + channelManager.addListener( (Listener)mMapService ); + init(); } diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java index 7e4887b1..db45d1d9 100644 --- a/src/source/tuner/TunerNode.java +++ b/src/source/tuner/TunerNode.java @@ -33,6 +33,7 @@ import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeListener; import spectrum.SpectrumFrame; import controller.BaseNode; +import controller.ResourceManager; public class TunerNode extends BaseNode implements FrequencyChangeListener { @@ -112,9 +113,11 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener @Override public void actionPerformed( ActionEvent e ) { - SpectrumFrame frame = - new SpectrumFrame( getModel().getResourceManager(), - getTuner() ); + ResourceManager rm = getModel().getResourceManager(); + + SpectrumFrame frame = new SpectrumFrame( rm.getChannelManager(), + rm.getController(), rm.getPlaylistManager(), + rm.getSettingsManager(), getTuner() ); } } ); diff --git a/src/spectrum/ChannelOverlayPanel.java b/src/spectrum/ChannelOverlayPanel.java index 061f7ef8..bd40571c 100644 --- a/src/spectrum/ChannelOverlayPanel.java +++ b/src/spectrum/ChannelOverlayPanel.java @@ -33,7 +33,7 @@ import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; -import controller.ResourceManager; +import settings.SettingsManager; public class ChannelOverlayPanel extends JPanel implements SettingChangeListener { @@ -56,16 +56,21 @@ public class ChannelOverlayPanel extends JPanel implements SettingChangeListener //the frequency labels private float mSpectrumInset = 20.0f; - private ResourceManager mResourceManager; + private SettingsManager mSettingsManager; /** * 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 ) + public ChannelOverlayPanel( SettingsManager settingsManager ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; + + if( mSettingsManager != null ) + { + mSettingsManager.addListener( this ); + } //Set the background transparent, so the spectrum display can be seen setOpaque( false ); @@ -76,7 +81,12 @@ public class ChannelOverlayPanel extends JPanel implements SettingChangeListener public void dispose() { - mResourceManager = null; + if( mSettingsManager != null ) + { + mSettingsManager.removeListener( this ); + } + + mSettingsManager = null; } public void setCursorLocation( Point point ) @@ -112,9 +122,7 @@ public class ChannelOverlayPanel extends JPanel implements SettingChangeListener */ private Color getColor( ColorSettingName name ) { - ColorSetting setting = - mResourceManager.getSettingsManager() - .getColorSetting( name ); + ColorSetting setting = mSettingsManager.getColorSetting( name ); return setting.getColor(); } diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index 5becccf8..a3bd460a 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -61,7 +61,7 @@ public class ChannelSpectrumPanel extends JPanel private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; +// private ResourceManager mResourceManager; private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.REAL ); private DFTResultsConverter mDFTConverter = new RealDecibelConverter(); private JLayeredPane mLayeredPane; @@ -78,15 +78,22 @@ public class ChannelSpectrumPanel extends JPanel Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true ); private AtomicBoolean mEnabled = new AtomicBoolean(); + + private SettingsManager mSettingsManager; - public ChannelSpectrumPanel( ResourceManager resourceManager ) + public ChannelSpectrumPanel( SettingsManager settingsManager ) { - mResourceManager = resourceManager; - mResourceManager.getSettingsManager().addListener( this ); - mSpectrumPanel = new SpectrumPanel( mResourceManager ); + mSettingsManager = settingsManager; + + if( mSettingsManager != null ) + { + mSettingsManager.addListener( this ); + } + + mSpectrumPanel = new SpectrumPanel( mSettingsManager ); mSpectrumPanel.setAveraging( 1 ); - mOverlayPanel = new ChannelOverlayPanel( mResourceManager ); + mOverlayPanel = new ChannelOverlayPanel( mSettingsManager ); mDFTProcessor.addConverter( mDFTConverter ); mDFTConverter.addListener( mSpectrumPanel ); @@ -103,7 +110,12 @@ public class ChannelSpectrumPanel extends JPanel mDFTProcessor.dispose(); - mResourceManager = null; + if( mSettingsManager != null ) + { + mSettingsManager.removeListener( this ); + } + + mSettingsManager = null; mCurrentChannel = null; mDFTProcessor = null; mSpectrumPanel = null; @@ -338,21 +350,19 @@ public class ChannelSpectrumPanel extends JPanel */ JMenu colorMenu = new JMenu( "Color" ); - SettingsManager sm = mResourceManager.getSettingsManager(); - - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_CURSOR ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_LINE ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_BACKGROUND ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_GRADIENT_BOTTOM ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_GRADIENT_TOP ) ); contextMenu.add( colorMenu ); diff --git a/src/spectrum/OverlayPanel.java b/src/spectrum/OverlayPanel.java index 1469043f..965d72cb 100644 --- a/src/spectrum/OverlayPanel.java +++ b/src/spectrum/OverlayPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,7 @@ ******************************************************************************/ package spectrum; +import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; @@ -24,6 +25,7 @@ import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; +import java.awt.Stroke; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.geom.Line2D; @@ -41,14 +43,15 @@ import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; +import settings.SettingsManager; import source.tuner.TunerChannel; import source.tuner.frequency.FrequencyChangeEvent; import source.tuner.frequency.FrequencyChangeListener; -import controller.ResourceManager; import controller.channel.Channel; import controller.channel.Channel.ChannelType; import controller.channel.ChannelEvent; import controller.channel.ChannelEventListener; +import controller.channel.ChannelManager; public class OverlayPanel extends JPanel implements ChannelEventListener, @@ -60,8 +63,20 @@ public class OverlayPanel extends JPanel private final static Logger mLog = LoggerFactory.getLogger( OverlayPanel.class ); + private final static RenderingHints RENDERING_HINTS = + new RenderingHints( RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON ); + + static + { + RENDERING_HINTS.put( RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY ); + } + + private final static BasicStroke DASHED_STROKE = new BasicStroke(0.8f, + BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 5.0f, new float[] {5.0f}, 0.0f); + private static DecimalFormat CURSOR_FORMAT = new DecimalFormat( "000.00000" ); - private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.0" ); private long mFrequency = 0; private int mBandwidth = 0; private Point mCursorLocation = new Point( 0, 0 ); @@ -92,18 +107,31 @@ public class OverlayPanel extends JPanel //Defines the offset at the bottom of the spectral display to account for //the frequency labels private double mSpectrumInset = 20.0d; - private LabelSizeMonitor mLabelSizeMonitor = new LabelSizeMonitor(); + private LabelSizeManager mLabelSizeMonitor = new LabelSizeManager(); - private ResourceManager mResourceManager; - + private SettingsManager mSettingsManager; + private ChannelManager mChannelManager; /** * Translucent overlay panel for displaying channel configurations, * processing channels, selected channels, frequency labels and lines, and * a cursor with a frequency readout. */ - public OverlayPanel( ResourceManager resourceManager ) + public OverlayPanel( SettingsManager settingsManager, + ChannelManager channelManager ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; + + if( mSettingsManager != null ) + { + mSettingsManager.addListener( this ); + } + + mChannelManager = channelManager; + + if( mChannelManager != null ) + { + mChannelManager.addListener( this ); + } addComponentListener( mLabelSizeMonitor ); @@ -116,12 +144,27 @@ public class OverlayPanel extends JPanel public void dispose() { + if( mChannelManager != null ) + { + mChannelManager.removeListener( this ); + } + + mChannelManager = null; + mChannels.clear(); mVisibleChannels.clear(); - mResourceManager = null; + if( mSettingsManager != null ) + { + mSettingsManager.removeListener( this ); + } + + mSettingsManager = null; } + /** + * Sets/changes the DFT bin size + */ public void setDFTSize( DFTSize size ) { mDFTSize = size; @@ -160,13 +203,20 @@ public class OverlayPanel extends JPanel * 3 8x Zoom * 4 16x Zoom * 5 32x Zoom + * 6 64x Zoom * - * @param zoom level, 0 - 5. + * @param zoom level, 0 - 6. * @param offset into the FFT bins for start zoom window */ - public void setZoom( int zoom, int offset ) + public void setZoom( int zoom ) { mZoom = zoom; + + mLabelSizeMonitor.update(); + } + + public void setZoomWindowOffset( int offset ) + { mDFTZoomWindowOffset = offset; } @@ -197,9 +247,7 @@ public class OverlayPanel extends JPanel */ private Color getColor( ColorSettingName name ) { - ColorSetting setting = - mResourceManager.getSettingsManager() - .getColorSetting( name ); + ColorSetting setting = mSettingsManager.getColorSetting( name ); return setting.getColor(); } @@ -252,14 +300,7 @@ public class OverlayPanel extends JPanel 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 ); + graphics.setRenderingHints( RENDERING_HINTS ); drawFrequencies( graphics ); drawChannels( graphics ); @@ -279,7 +320,7 @@ public class OverlayPanel extends JPanel mColorSpectrumCursor ); String frequency = CURSOR_FORMAT.format( - getFrequencyFromAxis( mCursorLocation.getX() ) / 1000000.0D ); + getFrequencyFromAxis( mCursorLocation.getX() ) / 1E6D ); graphics.drawString( frequency , mCursorLocation.x + 5, @@ -298,26 +339,28 @@ public class OverlayPanel extends JPanel */ private void drawFrequencies( Graphics2D graphics ) { + Stroke currentStroke = graphics.getStroke(); + + graphics.setStroke( DASHED_STROKE ); + long minFrequency = getMinDisplayFrequency(); long maxFrequency = getMaxDisplayFrequency(); + //Frequency increments for label and tick spacing + int label = mLabelSizeMonitor.getLabelIncrement( graphics ); int major = mLabelSizeMonitor.getMajorTickIncrement( graphics ); int minor = mLabelSizeMonitor.getMinorTickIncrement( graphics ); - int label = mLabelSizeMonitor.getLabelIncrement( graphics ); - - long start = minFrequency - ( minFrequency % label ); - - long frequency = start; + + //Adjust the start frequency to a multiple of the minor tick spacing + long frequency = minFrequency - ( minFrequency % minor ); while( frequency < maxFrequency ) { - int offset = (int)( frequency - start ); - - if( offset % label == 0 ) + if( frequency % label == 0 ) { drawFrequencyLineAndLabel( graphics, frequency ); } - else if( offset % major == 0 ) + else if( frequency % major == 0 ) { drawTickLine( graphics, frequency, true ); } @@ -328,6 +371,8 @@ public class OverlayPanel extends JPanel frequency += minor; } + + graphics.setStroke( currentStroke ); } /** @@ -356,7 +401,7 @@ public class OverlayPanel extends JPanel double xAxis = getAxisFromFrequency( frequency ); double start = getSize().getHeight() - mSpectrumInset; - double end = start + ( major ? 6.0d : 3.0d ); + double end = start + ( major ? 9.0d : 3.0d ); graphics.draw( new Line2D.Double( xAxis, start, xAxis, end ) ); } @@ -414,7 +459,8 @@ public class OverlayPanel extends JPanel } /** - * Returns the frequency corresponding to the x-axis value + * Returns the frequency corresponding to the x-axis value using the current + * zoom level. */ public long getFrequencyFromAxis( double xAxis ) { @@ -422,7 +468,15 @@ public class OverlayPanel extends JPanel double offset = xAxis / width; - return getMinFrequency() + Math.round( (double)mBandwidth * offset ); + long frequency = getMinDisplayFrequency() + + Math.round( (double)getDisplayBandwidth() * offset ); + + if( frequency > ( getMaxFrequency() ) ) + { + frequency = getMaxFrequency(); + } + + return frequency; } /** @@ -432,16 +486,14 @@ public class OverlayPanel extends JPanel double xaxis, long frequency ) { - String label = mFrequencyFormat.format( (float)frequency / 1000000.0f ); + String label = mLabelSizeMonitor.format( frequency ); - FontMetrics fontMetrics = graphics.getFontMetrics( this.getFont() ); + FontMetrics fontMetrics = graphics.getFontMetrics( this.getFont() ); Rectangle2D rect = fontMetrics.getStringBounds( label, graphics ); float xOffset = (float)rect.getWidth() / 2; -// graphics.drawString( label, (float)( xaxis - xOffset ), -// (float)( getSize().getHeight() - ( mSpectrumInset * 0.2d ) ) ); graphics.drawString( label, (float)( xaxis - xOffset ), (float)( getSize().getHeight() - 2.0f ) ); } @@ -598,24 +650,15 @@ public class OverlayPanel extends JPanel @Override public void frequencyChanged( FrequencyChangeEvent event ) { - mLabelSizeMonitor.frequencyChanged( event ); - switch( event.getAttribute() ) { case SAMPLE_RATE: mBandwidth = event.getValue().intValue(); - - if( mBandwidth < 200000 ) - { - mFrequencyFormat = new DecimalFormat( "0.00" ); - } - else - { - mFrequencyFormat = new DecimalFormat( "0.0" ); - } + mLabelSizeMonitor.update(); break; case FREQUENCY: mFrequency = event.getValue().longValue(); + mLabelSizeMonitor.update(); break; default: break; @@ -750,186 +793,134 @@ public class OverlayPanel extends JPanel } @Override - public void settingDeleted( Setting setting ) - { - // TODO Auto-generated method stub - } + public void settingDeleted( Setting setting ) { /* not implemented */ } /** - * Monitors the display for resize events so that we can calculate how many - * frequency labels will fit within the current screen real estate + * Calculates correct spacing and format for frequency labels and major/minor + * tick lines based on current frequency, bandwidth, zoom and screen size. */ - public class LabelSizeMonitor implements ComponentListener, - FrequencyChangeListener + public class LabelSizeManager implements ComponentListener { - private static final int MAJOR_TICK_MINIMUM = 10000; //10 kHz - private static final int MINOR_TICK_MINIMUM = 1000; //1 kHz - private static final int TICK_SPACING_MINIMUM = 10; //pixels + private static final double LABEL_FILL_THRESHOLD = 0.5d; - private boolean mUpdateRequired = true; - private LabelDisplay mLabelDisplay = LabelDisplay.DIGIT_3; - private int mMajorTickIncrement; - private int mMinorTickIncrement; - private int mLabelIncrement; + private DecimalFormat mFrequencyFormat = new DecimalFormat( "0.0" ); + private boolean mUpdateRequired = true; + private int mLabelIncrement = 1; + private int mMajorTickIncrement = 1; + private int mMinorTickIncrement = 1; + + public String format( long frequency ) + { + return mFrequencyFormat.format( (double)frequency / 1E6D ); + } + + private void setPrecision( int precision ) + { + if( precision < 1 ) + { + precision = 1; + } + + if( precision > 5 ) + { + precision = 5; + } + + mFrequencyFormat.setMinimumFractionDigits( precision ); + mFrequencyFormat.setMaximumFractionDigits( precision ); + } + private void update( Graphics2D graphics ) { if( mUpdateRequired ) { - double width = OverlayPanel.this.getSize().getWidth(); - - int major = MAJOR_TICK_MINIMUM; - - while( width / ( (double)mBandwidth / - (double)major ) < TICK_SPACING_MINIMUM ) - { - major *= 10; - } - - mMajorTickIncrement = major; - - int minor = MINOR_TICK_MINIMUM; - - while( width / ( (double)mBandwidth / - (double)minor ) < TICK_SPACING_MINIMUM ) - { - minor *= 10; - } - - if( minor == major ) - { - minor = (int)( major / 2 ); - } - - mMinorTickIncrement = minor; + //Set maximum precision as a starting point + setPrecision( 5 ); FontMetrics fontMetrics = graphics.getFontMetrics( OverlayPanel.this.getFont() ); - Rectangle2D labelDimension = fontMetrics.getStringBounds( - mLabelDisplay.getExample(), graphics ); - - int maxLabelCount = (int)( width / labelDimension.getWidth() ); + int maxLabelWidth = fontMetrics.stringWidth( format( getMaxDisplayFrequency() ) ); + + double maxLabels = ( (double)OverlayPanel.this.getWidth() * + LABEL_FILL_THRESHOLD ) / + (double)maxLabelWidth; + + //Calculate the next smallest base 10 value for the major increment + int power = (int)Math.log10( (double)getDisplayBandwidth() / maxLabels ); + + //Set the number of decimal places to display in frequency labels + int precision = 5 - power; - int label = major; + int start = (int)Math.pow( 10.0, power + 1 ); - while( ( (double)mBandwidth / (double)label ) > maxLabelCount ) - { - label += major; - } + int minimum = (int)Math.pow( 10.0, power ); + + int labelIncrement = start; + + while( ( (double)getDisplayBandwidth() / + (double)labelIncrement ) < maxLabels && + labelIncrement >= minimum ) + { + labelIncrement /= 2; + precision++; + } - mLabelIncrement = label; - - mUpdateRequired = false; + if( labelIncrement == minimum ) + { + precision = 5 - power; + } + + setPrecision( precision ); + + mLabelIncrement = labelIncrement; + mMajorTickIncrement = labelIncrement / 2; + mMinorTickIncrement = labelIncrement / 10; + + mUpdateRequired = false; } } + /** + * Forces the display to update the label and frequency display + * calculations + */ + public void update() + { + mUpdateRequired = true; + } + public int getMajorTickIncrement( Graphics2D graphics ) { + //Check to see if a calculation update is scheduled update( graphics ); return mMajorTickIncrement; } - + public int getMinorTickIncrement( Graphics2D graphics ) { - update( graphics ); - return mMinorTickIncrement; } public int getLabelIncrement( Graphics2D graphics ) { - update( graphics ); - return mLabelIncrement; } - public LabelDisplay getLabelDisplay() - { - return mLabelDisplay; - } - @Override public void componentResized( ComponentEvent arg0 ) { - mUpdateRequired = true; + update(); } public void componentHidden( ComponentEvent arg0 ) {} public void componentMoved( ComponentEvent arg0 ) {} public void componentShown( ComponentEvent arg0 ) {} - @Override - public void frequencyChanged( FrequencyChangeEvent event ) - { - switch( event.getAttribute() ) - { - case FREQUENCY: - LabelDisplay display = LabelDisplay - .fromFrequency( event.getValue().longValue() ); - - if( mLabelDisplay != display ) - { - mLabelDisplay = display; - mUpdateRequired = true; - } - break; - case SAMPLE_RATE: - mUpdateRequired = true; - break; - default: - break; - } - } } - /** - * Frequency display formats for determining label sizing and value formatting - */ - public enum LabelDisplay - { - DIGIT_1( " 9.9 " ), - DIGIT_2( " 99.9 " ), - DIGIT_3( " 999.9 " ), - DIGIT_4( " 9999.9 " ), - DIGIT_5( " 99999.9 " ); - - private String mExample; - - private LabelDisplay( String example ) - { - mExample = example; - } - - public String getExample() - { - return mExample; - } - - public static LabelDisplay fromFrequency( long frequency ) - { - if( frequency < 10000000l ) //10 MHz - { - return LabelDisplay.DIGIT_1; - } - else if( frequency < 100000000l ) //100 MHz - { - return LabelDisplay.DIGIT_2; - } - else if( frequency < 1000000000l ) //1,000 MHz - { - return LabelDisplay.DIGIT_3; - } - else if( frequency < 10000000000l ) //10,000 MHz - { - return LabelDisplay.DIGIT_4; - } - - return LabelDisplay.DIGIT_5; - } - } - public enum ChannelDisplay { ALL, ENABLED, NONE; diff --git a/src/spectrum/SpectralDisplayPanel.java b/src/spectrum/SpectralDisplayPanel.java index 5ba4ffc9..4b1252c6 100644 --- a/src/spectrum/SpectralDisplayPanel.java +++ b/src/spectrum/SpectralDisplayPanel.java @@ -25,10 +25,7 @@ import java.awt.event.ActionListener; 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.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; import java.text.DecimalFormat; import java.util.ArrayList; import java.util.Hashtable; @@ -48,6 +45,7 @@ import javax.swing.JSplitPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.event.MouseInputAdapter; import module.decode.DecoderType; import net.miginfocom.swing.MigLayout; @@ -55,13 +53,12 @@ import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import playlist.PlaylistManager; import sample.Listener; import sample.SampleType; import sample.complex.ComplexBuffer; import settings.ColorSetting.ColorSettingName; import settings.ColorSettingMenuItem; -import settings.Setting; -import settings.SettingChangeListener; import settings.SettingsManager; import source.SourceException; import source.tuner.Tuner; @@ -81,28 +78,17 @@ import spectrum.menu.SmoothingTypeItem; import com.jidesoft.swing.JideSplitPane; -import controller.ResourceManager; +import controller.ConfigurationControllerModel; import controller.channel.Channel; -import controller.channel.ChannelEvent; -import controller.channel.ChannelEventListener; +import controller.channel.ChannelManager; import controller.channel.ChannelUtils; import dsp.filter.Window.WindowType; import dsp.filter.smoothing.SmoothingFilter.SmoothingType; -/** - * Spectral Display Panel - comprises a DFTProcessor coupled with a waterfall - * display and a spectrum display, with an overlay of frequency data and channel - * data. - * - * Processes float or complex sample data and displays the frequency content - * in the spectral and waterfall displays. - */ public class SpectralDisplayPanel extends JPanel - implements ChannelEventListener, - Listener, + implements Listener, FrequencyChangeListener, IDFTWidthChangeProcessor, - SettingChangeListener, TunerSelectionListener { private static final long serialVersionUID = 1L; @@ -110,10 +96,10 @@ public class SpectralDisplayPanel extends JPanel private final static Logger mLog = LoggerFactory.getLogger( SpectralDisplayPanel.class ); + private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" ); + public static final int NO_ZOOM = 0; public static final int MAX_ZOOM = 6; - - private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" ); private DFTSize mDFTSize = DFTSize.FFT04096; private int mZoom = 0; @@ -126,13 +112,42 @@ public class SpectralDisplayPanel extends JPanel private OverlayPanel mOverlayPanel; private DFTProcessor mDFTProcessor; private DFTResultsConverter mDFTConverter; - private ResourceManager mResourceManager; + private PlaylistManager mPlaylistManager; + private SettingsManager mSettingsManager; + private ConfigurationControllerModel mController; private Tuner mTuner; - public SpectralDisplayPanel( ResourceManager resourceManager ) + /** + * Spectral Display Panel provides a frequency component display with a + * historical waterfall display and a transparent overlay to show frequency, + * cursor and channel information. + * + * Mouse scrolling and zooming are supported and the waterfall display can + * be paused. + * + * Complex sample buffers are processed by a DFTProcessor and the output of + * the DFT is translated to decibels for display in the spectrum and + * waterfall components. + */ + public SpectralDisplayPanel( ChannelManager channelManager, + ConfigurationControllerModel controller, + PlaylistManager playlistManager, + SettingsManager settingsManager ) { - mResourceManager = resourceManager; + mController = controller; + mPlaylistManager = playlistManager; + mSettingsManager = settingsManager; + mSpectrumPanel = new SpectrumPanel( mSettingsManager ); + mOverlayPanel = new OverlayPanel( mSettingsManager, channelManager ); + mWaterfallPanel = new WaterfallPanel( mSettingsManager ); + + //Register to receive tuner selection events + if( mController != null ) + { + mController.addListener( (TunerSelectionListener)this ); + } + init(); } @@ -140,8 +155,15 @@ public class SpectralDisplayPanel extends JPanel { /* De-register from receiving samples when the window closes */ clearTuner(); - - mResourceManager.getChannelManager().removeListener( this ); + + if( mController != null ) + { + mController.removeListener( (TunerSelectionListener)this ); + } + + mController = null; + mPlaylistManager = null; + mSettingsManager = null; mDFTProcessor.dispose(); mDFTProcessor = null; @@ -158,7 +180,6 @@ public class SpectralDisplayPanel extends JPanel mOverlayPanel.dispose(); mOverlayPanel = null; - mResourceManager = null; mTuner = null; } @@ -188,7 +209,7 @@ public class SpectralDisplayPanel extends JPanel } /** - * Sets the current zoom level (2^zoom) + * Sets the current zoom level which will be 2 to the power of zoom (2^zoom) * * 0 No Zoom * 1 2x Zoom @@ -198,31 +219,82 @@ public class SpectralDisplayPanel extends JPanel * 5 32x Zoom * 6 64x Zoom * - * @param zoom level, 0 - 5. + * @param zoom level, 0 - 6. * @param frequency under the mouse to maintain while zooming * @param xAxisOffset where to maintain the frequency under the mouse */ public void setZoom( int zoom, long frequency, double windowOffset ) { + if( zoom < NO_ZOOM ) + { + zoom = NO_ZOOM; + } + else if( zoom > MAX_ZOOM ) + { + zoom = MAX_ZOOM; + } + if( zoom != mZoom ) { mZoom = zoom; - double zoomTotal = Math.pow( 2.0, mZoom ); + //Calculate the bin offset that would place the reference frequency + //at the left edge of the zoom window. + double binOffsetToFrequency = getBinOffset( frequency ); - double overallOffset = getBinOffset( frequency ); - double windowBinOffset = ( (double)mDFTSize.getSize() / - (double)zoomTotal ) * - windowOffset; + //Calculate the bin offset into the newly sized zoom window that + //would place the frequency in the same proportional window location + //that it was in the previous zoom size + double windowBinOffset = (double)getZoomWindowSizeInBins() * windowOffset; + + //Set the overall offset to place the reference frequency in the + //same location in the newly zoomed window + double offset = binOffsetToFrequency - windowBinOffset; - mDFTZoomWindowOffset = (int)( overallOffset - windowBinOffset ); + mSpectrumPanel.setZoom( mZoom ); + mOverlayPanel.setZoom( mZoom ); + mWaterfallPanel.setZoom( mZoom ); - mSpectrumPanel.setZoom( mZoom, mDFTZoomWindowOffset ); - mOverlayPanel.setZoom( mZoom, mDFTZoomWindowOffset ); - mWaterfallPanel.setZoom( mZoom, mDFTZoomWindowOffset ); + setZoomWindowOffset( offset ); } } + /** + * Sets the offset (in DFT bins) to the first bin that the zoom window displays + */ + public void setZoomWindowOffset( double offset ) + { + if( offset < 0 ) + { + offset = 0; + } + + if( offset > ( mDFTSize.getSize() - getZoomWindowSizeInBins() ) ) + { + offset = mDFTSize.getSize() - getZoomWindowSizeInBins(); + } + + mDFTZoomWindowOffset = (int)offset; + + mSpectrumPanel.setZoomWindowOffset( mDFTZoomWindowOffset ); + mOverlayPanel.setZoomWindowOffset( mDFTZoomWindowOffset ); + mWaterfallPanel.setZoomWindowOffset( mDFTZoomWindowOffset ); + } + + /** + * Calculates the size of the current zoom window in DFT bins + */ + private int getZoomWindowSizeInBins() + { + return mDFTSize.getSize() / getZoomMultiplier(); + } + + + public int getZoomMultiplier() + { + return (int)Math.pow( 2.0, mZoom ); + } + /** * Calculates the overall offset of the frequency from the current minimum * frequency in terms of total FFT width @@ -231,39 +303,18 @@ public class SpectralDisplayPanel extends JPanel */ private double getBinOffset( long frequency ) { + double offset = 0.0; + if( mOverlayPanel.containsFrequency( frequency ) ) { - return (double)mDFTSize.getSize() * + offset = (double)mDFTSize.getSize() * ( (double)( frequency - mOverlayPanel.getMinFrequency() ) / (double)mOverlayPanel.getBandwidth() ); } - return 0.0; + return offset; } - /** - * 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( mOverlayPanel != null ) - { - mOverlayPanel.settingChanged( setting ); - } - - if( mWaterfallPanel != null ) - { - mWaterfallPanel.settingChanged( setting ); - } - - if( mSpectrumPanel != null ) - { - mSpectrumPanel.settingChanged( setting ); - } - } - /** * Overrides JComponent method to return false, since we have overlapping * panels with the spectrum and channel panels @@ -277,9 +328,6 @@ public class SpectralDisplayPanel extends JPanel { setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); - //Register for any future settings (ie color) changes - mResourceManager.getSettingsManager().addListener( this ); - /** * The layered pane holds the overlapping spectrum and channel panels * and manages the sizing of each panel with the resize listener @@ -293,11 +341,6 @@ public class SpectralDisplayPanel extends JPanel */ MouseEventProcessor mouser = new MouseEventProcessor(); - //Create the spectrum panel - mSpectrumPanel = new SpectrumPanel( mResourceManager ); - - //Create the overlay panel - mOverlayPanel = new OverlayPanel( mResourceManager ); mOverlayPanel.addMouseListener( mouser ); mOverlayPanel.addMouseMotionListener( mouser ); mOverlayPanel.addMouseWheelListener( mouser ); @@ -307,7 +350,6 @@ public class SpectralDisplayPanel extends JPanel mLayeredPanel.add( mOverlayPanel, new Integer( 1 ), 0 ); //Create the waterfall - mWaterfallPanel = new WaterfallPanel( mResourceManager ); mWaterfallPanel.addMouseListener( mouser ); mWaterfallPanel.addMouseMotionListener( mouser ); mWaterfallPanel.addMouseWheelListener( mouser ); @@ -355,22 +397,17 @@ public class SpectralDisplayPanel extends JPanel } /** - * Channel change events + * Complex sample buffer receive method */ @Override - public void channelChanged( ChannelEvent event ) - { - mOverlayPanel.channelChanged( event ); - } - - @Override public void receive( ComplexBuffer sampleBuffer ) { mDFTProcessor.receive( sampleBuffer ); } /** - * Tuner selected events + * Responds to tuner selection event by deregistering from the current + * complex sample buffer source and registering with the tuner argument. */ @Override public void tunerSelected( Tuner tuner ) @@ -391,8 +428,8 @@ public class SpectralDisplayPanel extends JPanel mSpectrumPanel.setSampleSize( mTuner.getSampleSize() ); - //Fire frequency and sample rate change events so that everyone - //can init + //Fire frequency and sample rate change events so that the spectrum + //and overlay panels can synchronize try { frequencyChanged( new FrequencyChangeEvent( @@ -449,26 +486,23 @@ public class SpectralDisplayPanel extends JPanel } /** - * Mouse event handler for the channel panel. + * Mouse event handler for the spectral display panel. */ - public class MouseEventProcessor implements MouseMotionListener, - MouseListener, - MouseWheelListener + public class MouseEventProcessor extends MouseInputAdapter { + private int mDFTZoomWindowOffsetAtDragStart = 0; + private int mDragStartX = 0; + private double mPixelsPerBin; + + public MouseEventProcessor() + { + } + @Override public void mouseWheelMoved( MouseWheelEvent e ) { int zoom = mZoom - e.getWheelRotation(); - if( zoom < NO_ZOOM ) - { - zoom = NO_ZOOM; - } - else if( zoom > MAX_ZOOM ) - { - zoom = MAX_ZOOM; - } - long frequency = mOverlayPanel.getFrequencyFromAxis( e.getX() ); double windowOffset = (double)e.getX() / (double)getWidth(); @@ -481,12 +515,47 @@ public class SpectralDisplayPanel extends JPanel { update( event ); } + @Override public void mouseDragged( MouseEvent event ) - { + { update( event ); + + int dragDistance = mDragStartX - event.getX(); + + double binDistance = (double)dragDistance / mPixelsPerBin; + + int offset = (int)( mDFTZoomWindowOffsetAtDragStart + binDistance ); + + if( offset < 0 ) + { + offset = 0; + } + + int maxOffset = mDFTSize.getSize() - ( mDFTSize.getSize() / getZoomMultiplier() ); + + if( offset > maxOffset ) + { + offset = maxOffset; + } + + setZoomWindowOffset( offset ); + } + @Override + public void mousePressed( MouseEvent e ) + { + mDragStartX = e.getX(); + + mDFTZoomWindowOffsetAtDragStart = mDFTZoomWindowOffset; + + mPixelsPerBin = (double)getWidth() / + ( (double)( mDFTSize.getSize() ) / + (double)getZoomMultiplier() ); } + /** + * Updates the cursor display while the mouse is performing actions + */ private void update( MouseEvent event ) { if( event.getComponent() == mOverlayPanel ) @@ -547,8 +616,7 @@ public class SpectralDisplayPanel extends JPanel for( Channel channel: channels ) { JMenu channelMenu = ChannelUtils.getContextMenu( - mResourceManager.getPlaylistManager(), - channel, SpectralDisplayPanel.this ); + mPlaylistManager, channel, SpectralDisplayPanel.this ); if( channelMenu != null ) { @@ -570,7 +638,7 @@ public class SpectralDisplayPanel extends JPanel for( DecoderType type: DecoderType.getPrimaryDecoders() ) { - decoderMenu.add( new DecoderItem( mResourceManager, + decoderMenu.add( new DecoderItem( mController, frequency, type ) ); @@ -587,30 +655,28 @@ public class SpectralDisplayPanel extends JPanel */ JMenu colorMenu = new JMenu( "Color" ); - SettingsManager sm = mResourceManager.getSettingsManager(); - - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.CHANNEL_CONFIG ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.CHANNEL_CONFIG_PROCESSING ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.CHANNEL_CONFIG_SELECTED ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_CURSOR ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_LINE ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_BACKGROUND ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_GRADIENT_BOTTOM ) ); - colorMenu.add( new ColorSettingMenuItem( sm, + colorMenu.add( new ColorSettingMenuItem( mSettingsManager, ColorSettingName.SPECTRUM_GRADIENT_TOP ) ); contextMenu.add( colorMenu ); @@ -734,11 +800,6 @@ public class SpectralDisplayPanel extends JPanel } } } - - @Override - public void mousePressed( MouseEvent e ) {} - @Override - public void mouseReleased( MouseEvent e ) {} } public class PauseItem extends JCheckBoxMenuItem @@ -859,15 +920,17 @@ public class SpectralDisplayPanel extends JPanel { private static final long serialVersionUID = 1L; - private ResourceManager mResourceManager; + private ConfigurationControllerModel mController; private long mFrequency; private DecoderType mDecoder; - public DecoderItem( ResourceManager manager, long frequency, DecoderType type ) + public DecoderItem( ConfigurationControllerModel controller, + long frequency, + DecoderType type ) { super( type.getDisplayString() ); - mResourceManager = manager; + mController = controller; mFrequency = frequency; mDecoder = type; @@ -876,13 +939,9 @@ public class SpectralDisplayPanel extends JPanel @Override public void actionPerformed( ActionEvent e ) { - mResourceManager.getController() - .createChannel( mFrequency, mDecoder ); + mController.createChannel( mFrequency, mDecoder ); } } ); } } - - @Override - public void settingDeleted( Setting setting ) {} } diff --git a/src/spectrum/SpectrumFrame.java b/src/spectrum/SpectrumFrame.java index 544df18a..2305721c 100644 --- a/src/spectrum/SpectrumFrame.java +++ b/src/spectrum/SpectrumFrame.java @@ -23,9 +23,13 @@ import java.awt.event.WindowListener; import javax.swing.JFrame; +import playlist.PlaylistManager; import net.miginfocom.swing.MigLayout; +import settings.SettingsManager; import source.tuner.Tuner; +import controller.ConfigurationControllerModel; import controller.ResourceManager; +import controller.channel.ChannelManager; public class SpectrumFrame extends JFrame implements WindowListener { @@ -34,23 +38,24 @@ public class SpectrumFrame extends JFrame implements WindowListener private ResourceManager mResourceManager; private SpectralDisplayPanel mSpectralDisplayPanel; - public SpectrumFrame( ResourceManager resourceManager, Tuner tuner ) + public SpectrumFrame( ChannelManager channelManager, + ConfigurationControllerModel controller, + PlaylistManager playlistManager, + SettingsManager settingsManager, + Tuner tuner ) { - mResourceManager = resourceManager; - setTitle( "SDRTRunk [" + tuner.getName() + "]" ); setBounds( 100, 100, 1280, 600 ); setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); setLayout( new MigLayout( "insets 0 0 0 0", "[grow]", "[grow]") ); - mSpectralDisplayPanel = new SpectralDisplayPanel( resourceManager ); + mSpectralDisplayPanel = new SpectralDisplayPanel( channelManager, + controller, playlistManager, settingsManager ); + mSpectralDisplayPanel.tunerSelected( tuner ); add( mSpectralDisplayPanel, "grow" ); - /* Register spectral display on channel manager to get channel updates */ - mResourceManager.getChannelManager().addListener( mSpectralDisplayPanel ); - /* Register a shutdown listener */ this.addWindowListener( this ); diff --git a/src/spectrum/SpectrumPanel.java b/src/spectrum/SpectrumPanel.java index 3ce5a131..53aeb69e 100644 --- a/src/spectrum/SpectrumPanel.java +++ b/src/spectrum/SpectrumPanel.java @@ -37,7 +37,7 @@ import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; -import controller.ResourceManager; +import settings.SettingsManager; import dsp.filter.smoothing.GaussianSmoothingFilter; import dsp.filter.smoothing.NoSmoothingFilter; import dsp.filter.smoothing.RectangularSmoothingFilter; @@ -59,11 +59,11 @@ public class SpectrumPanel extends JPanel new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); - //Color settings - 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"; + static + { + RENDERING_HINTS.put( RenderingHints.KEY_RENDERING, + RenderingHints.VALUE_RENDER_QUALITY ); + } private Color mColorSpectrumBackground; private Color mColorSpectrumGradientTop; @@ -86,29 +86,36 @@ public class SpectrumPanel extends JPanel private float mDBScale; private int mZoom = 0; - private int mZoomBinOffset = 0;; + private int mZoomWindowOffset = 0;; + + private SettingsManager mSettingsManager; - private ResourceManager mResourceManager; - - public SpectrumPanel( ResourceManager resourceManager ) + public SpectrumPanel( SettingsManager settingsManager ) { - mResourceManager = resourceManager; + mSettingsManager = settingsManager; - RENDERING_HINTS.put( RenderingHints.KEY_RENDERING, - RenderingHints.VALUE_RENDER_QUALITY ); + if( mSettingsManager != null ) + { + mSettingsManager.addListener( this ); + } setSampleSize( 16.0 ); mSmoothingFilter.setPointSize( SmoothingFilter.SMOOTHING_DEFAULT ); - getSettings(); + getColors(); setAveraging( mAveraging ); } public void dispose() { - mResourceManager = null; + if( mSettingsManager != null ) + { + mSettingsManager.removeListener( this ); + } + + mSettingsManager = null; } /** @@ -141,7 +148,8 @@ public class SpectrumPanel extends JPanel for( int x = 0; x < mDisplayFFTBins.length; x++ ) { - mDisplayFFTBins[ x ] += ( smoothedBins[ x ] - mDisplayFFTBins[ x ] ) * gain; + mDisplayFFTBins[ x ] += + ( smoothedBins[ x ] - mDisplayFFTBins[ x ] ) * gain; } } else @@ -158,8 +166,8 @@ public class SpectrumPanel extends JPanel super.paintComponent( g ); Graphics2D graphics = (Graphics2D) g; - graphics.setBackground( mColorSpectrumBackground ); + graphics.setBackground( mColorSpectrumBackground ); graphics.setRenderingHints( RENDERING_HINTS ); @@ -272,13 +280,11 @@ public class SpectrumPanel extends JPanel * * @param zoom level, 0 - 5. */ - public void setZoom( int zoom, int offset ) + public void setZoom( int zoom ) { assert( 0 <= zoom && zoom <= 5 ); mZoom = zoom; - - setZoomBinOffset( offset ); } /** @@ -286,9 +292,9 @@ public class SpectrumPanel extends JPanel * * @param offset */ - public void setZoomBinOffset( int offset ) + public void setZoomWindowOffset( int offset ) { - mZoomBinOffset = offset; + mZoomWindowOffset = offset; } /** @@ -330,7 +336,7 @@ public class SpectrumPanel extends JPanel repaint(); } - private void getSettings() + private void getColors() { mColorSpectrumBackground = getColor( ColorSettingName.SPECTRUM_BACKGROUND ); @@ -349,8 +355,7 @@ public class SpectrumPanel extends JPanel */ private Color getColor( ColorSettingName name ) { - ColorSetting setting = - mResourceManager.getSettingsManager().getColorSetting( name ); + ColorSetting setting = mSettingsManager.getColorSetting( name ); return setting.getColor(); } @@ -385,6 +390,15 @@ public class SpectrumPanel extends JPanel } } + private int getZoomMultiplier() + { + return (int)Math.pow( 2.0, mZoom ); + } + + /** + * Returns the DFT result bins, or a zoomed and offset version of the bins + * when the display is zoomed. + */ private float[] getBins() { if( mZoom == 0 ) @@ -393,16 +407,19 @@ public class SpectrumPanel extends JPanel } else { - int zoom = (int)Math.pow( 2.0, mZoom ); + int length = mDisplayFFTBins.length / getZoomMultiplier(); - int length = mDisplayFFTBins.length / zoom; + int offset = mZoomWindowOffset; - int offset = mZoomBinOffset; - - if( ( offset + length ) > mDisplayFFTBins.length ) + if( ( offset + length ) >= mDisplayFFTBins.length ) { offset = mDisplayFFTBins.length - length; } + + if( offset < 0 ) + { + offset = 0; + } return Arrays.copyOfRange( mDisplayFFTBins, offset, offset + length ); } diff --git a/src/spectrum/WaterfallPanel.java b/src/spectrum/WaterfallPanel.java index 4b491c40..3b8be259 100644 --- a/src/spectrum/WaterfallPanel.java +++ b/src/spectrum/WaterfallPanel.java @@ -1,6 +1,6 @@ /******************************************************************************* * SDR Trunk - * Copyright (C) 2014 Dennis Sheirer + * Copyright (C) 2014,2015 Dennis Sheirer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -40,26 +40,22 @@ import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; import settings.Setting; import settings.SettingChangeListener; -import controller.ResourceManager; +import settings.SettingsManager; -/** - * Produces a JPanel filled with a waterfall image derived from a continuous - * stream of received Discrete Fourier Transform (DFT) results from the - * DFTProcessor class. - */ public class WaterfallPanel extends JPanel implements DFTResultsListener, Pausable, SettingChangeListener { + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger( WaterfallPanel.class ); - private static final long serialVersionUID = 1L; private static DecimalFormat CURSOR_FORMAT = new DecimalFormat( "0.00000" ); - private static final String SPECTRUM_CURSOR = "spectrum_cursor"; private static final String PAUSED = "PAUSED"; private byte[] mPixels; + private byte[] mPausedPixels; private int mDFTSize = 4096; private int mImageHeight = 700; private MemoryImageSource mMemoryImageSource; @@ -70,29 +66,50 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, private Point mCursorLocation = new Point( 0, 0 ); private boolean mCursorVisible = false; private long mCursorFrequency = 0; - private AtomicBoolean mPaused = new AtomicBoolean(); + private boolean mPaused = false; private int mZoom = 0; - private int mDFTZoomOffset = 0; + private int mDFTZoomWindowOffset = 0; - private ResourceManager mResourceManager; - - public WaterfallPanel( ResourceManager resourceManager ) + private SettingsManager mSettingsManager; + + /** + * Displays a scrolling window of multiple DFT frequency bin outputs over + * time. Maps DFT frequency bin decibel values into a 256 bucket color map + * for display. + * + * @param settingsManager + */ + public WaterfallPanel( SettingsManager settingsManager ) { super(); - mResourceManager = resourceManager; + mSettingsManager = settingsManager; + + mSettingsManager.addListener( this ); mColorSpectrumCursor = getColor( ColorSettingName.SPECTRUM_CURSOR ); reset(); } - + + /** + * Prepares this instance for disposal + */ public void dispose() { - mResourceManager = null; + if( mSettingsManager != null ) + { + mSettingsManager.removeListener( this ); + } + + mSettingsManager = null; mMemoryImageSource = null; } - + + /** + * Resets the memory image source and byte backing array when the DFT point + * size has changed + */ private void reset() { mPixels = new byte[ mDFTSize * mImageHeight ]; @@ -110,18 +127,27 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, } /** - * Pausable interface - pauses updates to the screen + * Pausable interface - pauses updates to the waterfall */ public void setPaused( boolean paused ) { - mPaused.set( paused ); + if( paused ) + { + mPausedPixels = mPixels.clone(); + } + + mPaused = paused; repaint(); } + /** + * Returns current pause state + * @return true if paused, false otherwise + */ public boolean isPaused() { - return mPaused.get(); + return mPaused; } /** @@ -133,14 +159,31 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, * 3 8x Zoom * 4 16x Zoom * 5 32x Zoom + * 6 64x Zoom * - * @param zoom level, 0 - 5. + * @param zoom level, 0 - 6. * @param offset into the DFT bins for display */ - public void setZoom( int zoom, int offset ) + public void setZoom( int zoom ) { mZoom = zoom; - mDFTZoomOffset = offset; + } + + /** + * Multiplier for the current zoom level + */ + private int getZoomMultiplier() + { + return (int)Math.pow( 2.0, mZoom ); + } + + /** + * Sets the zoom window offset from zero + * @param offset in DFT bins + */ + public void setZoomWindowOffset( int offset ) + { + mDFTZoomWindowOffset = offset; } /** @@ -149,8 +192,7 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, */ private Color getColor( ColorSettingName name ) { - ColorSetting setting = mResourceManager.getSettingsManager() - .getColorSetting( name ); + ColorSetting setting = mSettingsManager.getColorSetting( name ); return setting.getColor(); } @@ -174,18 +216,37 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, } } + @Override + public void settingDeleted( Setting setting ) { /* Not implemented */ } + + /** + * Sets the display location of the cursor. Cursor location monitoring is + * handled external to this class. + * + * @param point + */ public void setCursorLocation( Point point ) { mCursorLocation = point; repaint(); } - + + /** + * Sets the current cursor display frequency. Cursor location frequency + * monitoring is handled external to this class. + * + * @param frequency + */ public void setCursorFrequency( long frequency ) { mCursorFrequency = frequency; } - + + /** + * Toggles the visibility of the cursor + * @param visible + */ public void setCursorVisible( boolean visible ) { mCursorVisible = visible; @@ -193,21 +254,34 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, repaint(); } + /** + * Calculates the x-axis pixel offset from zero where to start rendering the + * waterfall image + * + * @param multiplier - current zoom multiplier + * @return x-axis pixel offset + */ private int getPixelOffset( int multiplier ) { + int offset = 0; + if( mZoom != 0 ) { - return -(int)( (double)getWidth() * multiplier / - (double)mDFTSize * - (double)( mDFTZoomOffset ) ); + int zoomWidth = getWidth() * multiplier; + + offset = -(int)( (double)zoomWidth / (double)mDFTSize * + (double)( mDFTZoomWindowOffset ) ); } - - return 0; + + return offset; } + /** + * Renders the screen at each refresh + */ public void paintComponent( Graphics g ) { - int multiplier = (int)Math.pow( 2.0, mZoom ); + int multiplier = getZoomMultiplier(); int offset = getPixelOffset( multiplier ); @@ -220,10 +294,11 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, Graphics2D graphics = (Graphics2D) g; + graphics.setColor( mColorSpectrumCursor ); + if( mCursorVisible ) { - graphics.setColor( mColorSpectrumCursor ); graphics.draw( new Line2D.Float( mCursorLocation.x, 0, @@ -235,22 +310,57 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, graphics.drawString( frequency , mCursorLocation.x + 5, mCursorLocation.y ); - - if( mZoom != 0 ) - { - graphics.drawString( "Zoom: " + (int)Math.pow( 2.0, mZoom ) + "x", - mCursorLocation.x + 17, mCursorLocation.y + 11 ); - } } - if( mPaused.get() ) + if( mPaused ) { graphics.drawString( PAUSED, 20, 20 ); } + paintZoomIndicator( graphics ); + graphics.dispose(); } + /** + * When zoom level is greater than zero, paints a small indicator at the + * bottom center of the screen showing the location of the zoom window + * within the overall DFT results window + */ + private void paintZoomIndicator( Graphics2D graphics ) + { + if( mZoom != 0 ) + { + int width = getWidth() / 4; + + int x = ( getWidth() / 2 ) - ( width / 2 ); + + //Draw the outer window + graphics.drawRect( x, getHeight() - 12, width, 10 ); + + int zoomWidth = width / getZoomMultiplier(); + + int windowOffset = 0; + + if( mDFTZoomWindowOffset != 0 ) + { + windowOffset = (int)( ( (double)mDFTZoomWindowOffset / + (double)mDFTSize ) * width ); + } + + //Draw the zoom window + graphics.fillRect( x + windowOffset, getHeight() - 12, zoomWidth, 10 ); + + //Draw the zoom text + graphics.drawString( "Zoom: " + getZoomMultiplier() + "x", + x + width + 3, getHeight() - 2 ); + } + } + + /** + * Implements the DFT results listener interface method. This is the + * primary method for receiving new frequency bin results. + */ @Override public void receive( float[] update ) { @@ -297,37 +407,25 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener, mPixels[ x ] = (byte)value; } } - - if( !mPaused.get() ) + + //Task the swing event thread to update the display + EventQueue.invokeLater( new Runnable() { - EventQueue.invokeLater( new Runnable() - { - @Override - public void run() - { - if( mMemoryImageSource != null ) + @Override + public void run() + { + if( mMemoryImageSource != null ) + { + if( mPaused ) + { + mMemoryImageSource.newPixels( mPausedPixels, mColorModel, 0, mDFTSize ); + } + else { mMemoryImageSource.newPixels( mPixels, mColorModel, 0, mDFTSize ); } - } - } ); - } + } + } + } ); } - - public class Handler implements MouseListener - { - @Override - public void mouseClicked( MouseEvent e ) - { - //Display context menu to reset the display reset(); - } - - public void mousePressed( MouseEvent e ) {} - public void mouseReleased( MouseEvent e ) {} - public void mouseEntered( MouseEvent e ) {} - public void mouseExited( MouseEvent e ){} - } - - @Override - public void settingDeleted( Setting setting ) {} }