mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 16:47:31 -05:00
Spectrum/Waterfall display zoom feature - work in progress (issue #33)
Implements zoom in/out by mouse scroll wheel and by right-click context menu. Still have to add ability to drag spectrum left/right when zoomed in.
This commit is contained in:
@@ -38,6 +38,7 @@ 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 net.miginfocom.swing.MigLayout;
|
||||
|
||||
@@ -35,7 +35,7 @@ import source.tuner.frequency.FrequencyChangeEvent.Attribute;
|
||||
import spectrum.converter.DFTResultsConverter;
|
||||
import spectrum.converter.RealDecibelConverter;
|
||||
import spectrum.menu.AveragingItem;
|
||||
import spectrum.menu.FFTWidthItem;
|
||||
import spectrum.menu.DFTSizeItem;
|
||||
import spectrum.menu.FFTWindowTypeItem;
|
||||
import spectrum.menu.FrameRateItem;
|
||||
import spectrum.menu.SmoothingItem;
|
||||
@@ -377,9 +377,9 @@ public class ChannelSpectrumPanel extends JPanel
|
||||
JMenu fftWidthMenu = new JMenu( "FFT Width" );
|
||||
displayMenu.add( fftWidthMenu );
|
||||
|
||||
for( FFTWidth width: FFTWidth.values() )
|
||||
for( DFTSize width: DFTSize.values() )
|
||||
{
|
||||
fftWidthMenu.add( new FFTWidthItem( mDFTProcessor, width ) );
|
||||
fftWidthMenu.add( new DFTSizeItem( mDFTProcessor, width ) );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,9 @@ import dsp.filter.Window.WindowType;
|
||||
* 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<ComplexBuffer>, FrequencyChangeListener
|
||||
public class DFTProcessor implements Listener<ComplexBuffer>,
|
||||
FrequencyChangeListener,
|
||||
IDFTWidthChangeProcessor
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( DFTProcessor.class );
|
||||
@@ -59,8 +61,8 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
private ScheduledExecutorService mScheduler = Executors
|
||||
.newScheduledThreadPool( 1, new NamingThreadFactory( "spectrum dft" ) );
|
||||
|
||||
private FFTWidth mFFTWidth = FFTWidth.FFT04096;
|
||||
private FFTWidth mNewFFTWidth = FFTWidth.FFT04096;
|
||||
private DFTSize mDFTSize = DFTSize.FFT04096;
|
||||
private DFTSize mNewDFTSize = DFTSize.FFT04096;
|
||||
|
||||
private double[] mWindow;
|
||||
|
||||
@@ -68,7 +70,7 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
* with minimal bin leakage/smearing */
|
||||
private WindowType mWindowType = Window.WindowType.HANNING;
|
||||
|
||||
private FloatFFT_1D mFFT = new FloatFFT_1D( mFFTWidth.getWidth() );
|
||||
private FloatFFT_1D mFFT = new FloatFFT_1D( mDFTSize.getSize() );
|
||||
|
||||
private int mFrameRate;
|
||||
private int mSampleRate;
|
||||
@@ -112,12 +114,12 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
if( mSampleType == SampleType.COMPLEX )
|
||||
{
|
||||
mWindow = Window.getWindow( mWindowType,
|
||||
mFFTWidth.getWidth() * 2 );
|
||||
mDFTSize.getSize() * 2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
mWindow = Window.getWindow( mWindowType,
|
||||
mFFTWidth.getWidth() );
|
||||
mDFTSize.getSize() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,14 +142,14 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
* Queues an FFT size change request. The scheduled executor will apply
|
||||
* the change when it runs.
|
||||
*/
|
||||
public void setFFTSize( FFTWidth width )
|
||||
public void setDFTSize( DFTSize size )
|
||||
{
|
||||
mNewFFTWidth = width;
|
||||
mNewDFTSize = size;
|
||||
}
|
||||
|
||||
public FFTWidth getFFTWidth()
|
||||
public DFTSize getDFTSize()
|
||||
{
|
||||
return mFFTWidth;
|
||||
return mDFTSize;
|
||||
}
|
||||
|
||||
public int getFrameRate()
|
||||
@@ -444,9 +446,9 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
*/
|
||||
private void checkFFTSize()
|
||||
{
|
||||
if( mNewFFTWidth.getWidth() != mFFTWidth.getWidth() )
|
||||
if( mNewDFTSize.getSize() != mDFTSize.getSize() )
|
||||
{
|
||||
mFFTWidth = mNewFFTWidth;
|
||||
mDFTSize = mNewDFTSize;
|
||||
|
||||
calculateConsumptionRate();
|
||||
|
||||
@@ -454,14 +456,14 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
|
||||
if( mSampleType == SampleType.COMPLEX )
|
||||
{
|
||||
mPreviousFrame = new float[ mFFTWidth.getWidth() * 2 ];
|
||||
mPreviousFrame = new float[ mDFTSize.getSize() * 2 ];
|
||||
}
|
||||
else
|
||||
{
|
||||
mPreviousFrame = new float[ mFFTWidth.getWidth() ];
|
||||
mPreviousFrame = new float[ mDFTSize.getSize() ];
|
||||
}
|
||||
|
||||
mFFT = new FloatFFT_1D( mFFTWidth.getWidth() );
|
||||
mFFT = new FloatFFT_1D( mDFTSize.getSize() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,7 +499,7 @@ public class DFTProcessor implements Listener<ComplexBuffer>, FrequencyChangeLis
|
||||
( mSampleType == SampleType.COMPLEX ? 2.0f : 1.0f );
|
||||
|
||||
mFFTFloatsPerFrame = ( mSampleType == SampleType.COMPLEX ?
|
||||
mFFTWidth.getWidth() * 2 :
|
||||
mFFTWidth.getWidth() );
|
||||
mDFTSize.getSize() * 2 :
|
||||
mDFTSize.getSize() );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
******************************************************************************/
|
||||
package spectrum;
|
||||
|
||||
public enum FFTWidth
|
||||
public enum DFTSize
|
||||
{
|
||||
FFT00512( 512 ),
|
||||
FFT01024( 1024 ),
|
||||
@@ -29,12 +29,12 @@ public enum FFTWidth
|
||||
|
||||
private int mWidth;
|
||||
|
||||
private FFTWidth( int width )
|
||||
private DFTSize( int width )
|
||||
{
|
||||
mWidth = width;
|
||||
}
|
||||
|
||||
public int getWidth()
|
||||
public int getSize()
|
||||
{
|
||||
return mWidth;
|
||||
}
|
||||
7
src/spectrum/IDFTWidthChangeProcessor.java
Normal file
7
src/spectrum/IDFTWidthChangeProcessor.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package spectrum;
|
||||
|
||||
public interface IDFTWidthChangeProcessor
|
||||
{
|
||||
public void setDFTSize( DFTSize size );
|
||||
public DFTSize getDFTSize();
|
||||
}
|
||||
@@ -67,6 +67,10 @@ public class OverlayPanel extends JPanel
|
||||
private Point mCursorLocation = new Point( 0, 0 );
|
||||
private boolean mCursorVisible = false;
|
||||
|
||||
private DFTSize mDFTSize = DFTSize.FFT04096;
|
||||
private int mZoom = 0;
|
||||
private int mDFTZoomWindowOffset = 0;
|
||||
|
||||
/**
|
||||
* Colors used by this component
|
||||
*/
|
||||
@@ -118,6 +122,11 @@ public class OverlayPanel extends JPanel
|
||||
mResourceManager = null;
|
||||
}
|
||||
|
||||
public void setDFTSize( DFTSize size )
|
||||
{
|
||||
mDFTSize = size;
|
||||
}
|
||||
|
||||
public ChannelDisplay getChannelDisplay()
|
||||
{
|
||||
return mChannelDisplay;
|
||||
@@ -142,6 +151,25 @@ public class OverlayPanel extends JPanel
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current zoom level (2^zoom)
|
||||
*
|
||||
* 0 No Zoom
|
||||
* 1 2x Zoom
|
||||
* 2 4x Zoom
|
||||
* 3 8x Zoom
|
||||
* 4 16x Zoom
|
||||
* 5 32x Zoom
|
||||
*
|
||||
* @param zoom level, 0 - 5.
|
||||
* @param offset into the FFT bins for start zoom window
|
||||
*/
|
||||
public void setZoom( int zoom, int offset )
|
||||
{
|
||||
mZoom = zoom;
|
||||
mDFTZoomWindowOffset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the color settings from the settings manager
|
||||
*/
|
||||
@@ -237,7 +265,7 @@ public class OverlayPanel extends JPanel
|
||||
drawChannels( graphics );
|
||||
drawCursor( graphics );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draws a cursor on the panel, whenever the mouse is hovering over the
|
||||
* panel
|
||||
@@ -256,6 +284,12 @@ public class OverlayPanel extends JPanel
|
||||
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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -264,8 +298,8 @@ public class OverlayPanel extends JPanel
|
||||
*/
|
||||
private void drawFrequencies( Graphics2D graphics )
|
||||
{
|
||||
long minFrequency = getMinFrequency();
|
||||
long maxFrequency = getMaxFrequency();
|
||||
long minFrequency = getMinDisplayFrequency();
|
||||
long maxFrequency = getMaxDisplayFrequency();
|
||||
|
||||
int major = mLabelSizeMonitor.getMajorTickIncrement( graphics );
|
||||
int minor = mLabelSizeMonitor.getMinorTickIncrement( graphics );
|
||||
@@ -365,21 +399,18 @@ public class OverlayPanel extends JPanel
|
||||
/**
|
||||
* Returns the x-axis value corresponding to the frequency
|
||||
*/
|
||||
public double getAxisFromFrequency( long frequency )
|
||||
private double getAxisFromFrequency( long frequency )
|
||||
{
|
||||
double canvasMiddle = getSize().getWidth() / 2.0d;
|
||||
|
||||
//Determine frequency offset from middle
|
||||
double frequencyOffset = mFrequency - frequency;
|
||||
double frequencyOffset = frequency - getMinDisplayFrequency();
|
||||
|
||||
//Determine ratio of offset to bandwidth
|
||||
double ratio = frequencyOffset / (double)mBandwidth;
|
||||
double ratio = frequencyOffset / (double)getDisplayBandwidth();
|
||||
|
||||
//Calculate offset against the total width
|
||||
double xOffset = getSize().getWidth() * ratio;
|
||||
|
||||
//Apply the offset against the canvas middle
|
||||
return canvasMiddle - xOffset;
|
||||
double xOffset = (double)getSize().getWidth() * ratio;
|
||||
|
||||
return xOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -448,7 +479,7 @@ public class OverlayPanel extends JPanel
|
||||
double xAxis = getAxisFromFrequency( tunerChannel.getFrequency() );
|
||||
|
||||
double width = (double)( tunerChannel.getBandwidth() ) /
|
||||
(double)mBandwidth * getSize().getWidth();
|
||||
(double)getDisplayBandwidth() * getSize().getWidth();
|
||||
|
||||
Rectangle2D.Double box =
|
||||
new Rectangle2D.Double( xAxis - ( width / 2.0d ),
|
||||
@@ -646,11 +677,16 @@ public class OverlayPanel extends JPanel
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
public int getBandwidth()
|
||||
{
|
||||
return mBandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently displayed minimum frequency
|
||||
*/
|
||||
private long getMinFrequency()
|
||||
public long getMinFrequency()
|
||||
{
|
||||
return mFrequency - ( mBandwidth / 2 );
|
||||
}
|
||||
@@ -658,11 +694,38 @@ public class OverlayPanel extends JPanel
|
||||
/**
|
||||
* Currently displayed maximum frequency
|
||||
*/
|
||||
private long getMaxFrequency()
|
||||
public long getMaxFrequency()
|
||||
{
|
||||
return mFrequency + ( mBandwidth / 2 );
|
||||
}
|
||||
|
||||
public boolean containsFrequency( long frequency )
|
||||
{
|
||||
return Math.abs( mFrequency - frequency ) <= ( mBandwidth / 2 );
|
||||
}
|
||||
|
||||
private long getMinDisplayFrequency()
|
||||
{
|
||||
double bandwidthPerBin = (double)mBandwidth / (double)( mDFTSize.getSize() - 1 );
|
||||
|
||||
return getMinFrequency() + (int)( mDFTZoomWindowOffset * bandwidthPerBin );
|
||||
}
|
||||
|
||||
private long getMaxDisplayFrequency()
|
||||
{
|
||||
return getMinDisplayFrequency() + getDisplayBandwidth();
|
||||
}
|
||||
|
||||
private int getDisplayBandwidth()
|
||||
{
|
||||
if( mZoom != 0 )
|
||||
{
|
||||
return mBandwidth / (int)Math.pow( 2.0, mZoom );
|
||||
}
|
||||
|
||||
return mBandwidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of channel configs that contain the frequency within their
|
||||
* min/max frequency settings.
|
||||
|
||||
@@ -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,7 +17,6 @@
|
||||
******************************************************************************/
|
||||
package spectrum;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.EventQueue;
|
||||
@@ -28,18 +27,27 @@ 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;
|
||||
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JLayeredPane;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
|
||||
import module.decode.DecoderType;
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
@@ -50,7 +58,6 @@ import org.slf4j.LoggerFactory;
|
||||
import sample.Listener;
|
||||
import sample.SampleType;
|
||||
import sample.complex.ComplexBuffer;
|
||||
import settings.ColorSetting;
|
||||
import settings.ColorSetting.ColorSettingName;
|
||||
import settings.ColorSettingMenuItem;
|
||||
import settings.Setting;
|
||||
@@ -66,7 +73,7 @@ import spectrum.OverlayPanel.ChannelDisplay;
|
||||
import spectrum.converter.ComplexDecibelConverter;
|
||||
import spectrum.converter.DFTResultsConverter;
|
||||
import spectrum.menu.AveragingItem;
|
||||
import spectrum.menu.FFTWidthItem;
|
||||
import spectrum.menu.DFTSizeItem;
|
||||
import spectrum.menu.FFTWindowTypeItem;
|
||||
import spectrum.menu.FrameRateItem;
|
||||
import spectrum.menu.SmoothingItem;
|
||||
@@ -94,27 +101,25 @@ public class SpectralDisplayPanel extends JPanel
|
||||
implements ChannelEventListener,
|
||||
Listener<ComplexBuffer>,
|
||||
FrequencyChangeListener,
|
||||
IDFTWidthChangeProcessor,
|
||||
SettingChangeListener,
|
||||
TunerSelectionListener
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( SpectralDisplayPanel.class );
|
||||
|
||||
public static final int NO_ZOOM = 0;
|
||||
public static final int MAX_ZOOM = 6;
|
||||
|
||||
private static DecimalFormat sCURSOR_FORMAT = new DecimalFormat( "000.00000" );
|
||||
|
||||
/**
|
||||
* Colors used by this component
|
||||
*/
|
||||
private Color mColorChannelConfig;
|
||||
private Color mColorChannelConfigProcessing;
|
||||
private Color mColorChannelConfigSelected;
|
||||
private Color mColorSpectrumBackground;
|
||||
private Color mColorSpectrumCursor;
|
||||
private Color mColorSpectrumGradientBottom;
|
||||
private Color mColorSpectrumGradientTop;
|
||||
private Color mColorSpectrumLine;
|
||||
|
||||
private DFTSize mDFTSize = DFTSize.FFT04096;
|
||||
private int mZoom = 0;
|
||||
private int mDFTZoomWindowOffset = 0;
|
||||
|
||||
private JScrollPane mScrollPane;
|
||||
private JLayeredPane mLayeredPanel;
|
||||
private SpectrumPanel mSpectrumPanel;
|
||||
private WaterfallPanel mWaterfallPanel;
|
||||
@@ -128,8 +133,6 @@ public class SpectralDisplayPanel extends JPanel
|
||||
{
|
||||
mResourceManager = resourceManager;
|
||||
|
||||
setColors();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -159,56 +162,84 @@ public class SpectralDisplayPanel extends JPanel
|
||||
|
||||
mTuner = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Fetches the color settings from the settings manager
|
||||
* Queues an FFT size change request. The scheduled executor will apply
|
||||
* the change when it runs.
|
||||
*/
|
||||
private void setColors()
|
||||
public void setDFTSize( DFTSize size )
|
||||
{
|
||||
mColorChannelConfig = getColor( ColorSettingName.CHANNEL_CONFIG );
|
||||
mDFTProcessor.setDFTSize( size );
|
||||
mOverlayPanel.setDFTSize( size );
|
||||
mDFTSize = size;
|
||||
|
||||
mColorChannelConfigProcessing =
|
||||
getColor( ColorSettingName.CHANNEL_CONFIG_PROCESSING );
|
||||
|
||||
mColorChannelConfigSelected =
|
||||
getColor( ColorSettingName.CHANNEL_CONFIG_SELECTED );
|
||||
|
||||
mColorSpectrumCursor = getColor( ColorSettingName.SPECTRUM_CURSOR );
|
||||
|
||||
mColorSpectrumLine = getColor( ColorSettingName.SPECTRUM_LINE );
|
||||
|
||||
mColorSpectrumBackground =
|
||||
getColor( ColorSettingName.SPECTRUM_BACKGROUND );
|
||||
|
||||
mColorSpectrumGradientBottom =
|
||||
getColor( ColorSettingName.SPECTRUM_GRADIENT_BOTTOM );
|
||||
|
||||
mColorSpectrumGradientTop =
|
||||
getColor( ColorSettingName.SPECTRUM_GRADIENT_TOP );
|
||||
setZoom( 0, 0, 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public DFTSize getDFTSize()
|
||||
{
|
||||
return mDFTSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
public int getZoom()
|
||||
{
|
||||
return mZoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current zoom level (2^zoom)
|
||||
*
|
||||
* 0 No Zoom
|
||||
* 1 2x Zoom
|
||||
* 2 4x Zoom
|
||||
* 3 8x Zoom
|
||||
* 4 16x Zoom
|
||||
* 5 32x Zoom
|
||||
* 6 64x Zoom
|
||||
*
|
||||
* @param zoom level, 0 - 5.
|
||||
* @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 != mZoom )
|
||||
{
|
||||
mZoom = zoom;
|
||||
|
||||
double zoomTotal = Math.pow( 2.0, mZoom );
|
||||
|
||||
/**
|
||||
* Stores the named color setting
|
||||
*/
|
||||
private void setColor( ColorSettingName name, Color color, int translucency )
|
||||
{
|
||||
Color adjustedColor = ColorSetting.getTranslucent( color, translucency );
|
||||
|
||||
mResourceManager.getSettingsManager()
|
||||
.setColorSetting( name, adjustedColor );
|
||||
}
|
||||
double overallOffset = getBinOffset( frequency );
|
||||
double windowBinOffset = ( (double)mDFTSize.getSize() /
|
||||
(double)zoomTotal ) *
|
||||
windowOffset;
|
||||
|
||||
mDFTZoomWindowOffset = (int)( overallOffset - windowBinOffset );
|
||||
|
||||
mSpectrumPanel.setZoom( mZoom, mDFTZoomWindowOffset );
|
||||
mOverlayPanel.setZoom( mZoom, mDFTZoomWindowOffset );
|
||||
mWaterfallPanel.setZoom( mZoom, mDFTZoomWindowOffset );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the overall offset of the frequency from the current minimum
|
||||
* frequency in terms of total FFT width
|
||||
* @param frequency
|
||||
* @return
|
||||
*/
|
||||
private double getBinOffset( long frequency )
|
||||
{
|
||||
if( mOverlayPanel.containsFrequency( frequency ) )
|
||||
{
|
||||
return (double)mDFTSize.getSize() *
|
||||
( (double)( frequency - mOverlayPanel.getMinFrequency() ) /
|
||||
(double)mOverlayPanel.getBandwidth() );
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors for setting changes. Colors can be changed by external actions
|
||||
@@ -231,41 +262,6 @@ public class SpectralDisplayPanel extends JPanel
|
||||
{
|
||||
mSpectrumPanel.settingChanged( setting );
|
||||
}
|
||||
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting)setting;
|
||||
|
||||
switch( ((ColorSetting)setting).getColorSettingName() )
|
||||
{
|
||||
case CHANNEL_CONFIG:
|
||||
mColorChannelConfig = colorSetting.getColor();
|
||||
break;
|
||||
case CHANNEL_CONFIG_PROCESSING:
|
||||
mColorChannelConfigProcessing = colorSetting.getColor();
|
||||
break;
|
||||
case CHANNEL_CONFIG_SELECTED:
|
||||
mColorChannelConfigSelected = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_BACKGROUND:
|
||||
mColorSpectrumBackground = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_CURSOR:
|
||||
mColorSpectrumCursor = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_GRADIENT_BOTTOM:
|
||||
mColorSpectrumGradientBottom = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_GRADIENT_TOP:
|
||||
mColorSpectrumGradientTop = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_LINE:
|
||||
mColorSpectrumLine = colorSetting.getColor();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -304,6 +300,7 @@ public class SpectralDisplayPanel extends JPanel
|
||||
mOverlayPanel = new OverlayPanel( mResourceManager );
|
||||
mOverlayPanel.addMouseListener( mouser );
|
||||
mOverlayPanel.addMouseMotionListener( mouser );
|
||||
mOverlayPanel.addMouseWheelListener( mouser );
|
||||
|
||||
//Add the spectrum and channel panels to the layered panel
|
||||
mLayeredPanel.add( mSpectrumPanel, new Integer( 0 ), 0 );
|
||||
@@ -313,6 +310,7 @@ public class SpectralDisplayPanel extends JPanel
|
||||
mWaterfallPanel = new WaterfallPanel( mResourceManager );
|
||||
mWaterfallPanel.addMouseListener( mouser );
|
||||
mWaterfallPanel.addMouseMotionListener( mouser );
|
||||
mWaterfallPanel.addMouseWheelListener( mouser );
|
||||
|
||||
/* Attempt to set a 50/50 split preferred size for the split pane */
|
||||
double totalHeight = mLayeredPanel.getPreferredSize().getHeight() +
|
||||
@@ -330,7 +328,9 @@ public class SpectralDisplayPanel extends JPanel
|
||||
splitPane.add( mLayeredPanel );
|
||||
splitPane.add( mWaterfallPanel );
|
||||
|
||||
add( splitPane, "grow" );
|
||||
mScrollPane = new JScrollPane( splitPane );
|
||||
|
||||
add( mScrollPane, "grow" );
|
||||
|
||||
/**
|
||||
* Setup DFTProcessor to process samples and register the waterfall and
|
||||
@@ -451,8 +451,30 @@ public class SpectralDisplayPanel extends JPanel
|
||||
/**
|
||||
* Mouse event handler for the channel panel.
|
||||
*/
|
||||
public class MouseEventProcessor implements MouseMotionListener, MouseListener
|
||||
public class MouseEventProcessor implements MouseMotionListener,
|
||||
MouseListener,
|
||||
MouseWheelListener
|
||||
{
|
||||
@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();
|
||||
|
||||
setZoom( zoom, frequency, windowOffset );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved( MouseEvent event )
|
||||
@@ -631,9 +653,9 @@ public class SpectralDisplayPanel extends JPanel
|
||||
JMenu fftWidthMenu = new JMenu( "FFT Width" );
|
||||
displayMenu.add( fftWidthMenu );
|
||||
|
||||
for( FFTWidth width: FFTWidth.values() )
|
||||
for( DFTSize width: DFTSize.values() )
|
||||
{
|
||||
fftWidthMenu.add( new FFTWidthItem( mDFTProcessor, width ) );
|
||||
fftWidthMenu.add( new DFTSizeItem( SpectralDisplayPanel.this, width ) );
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -683,6 +705,18 @@ public class SpectralDisplayPanel extends JPanel
|
||||
displayMenu.add( smoothingMenu );
|
||||
}
|
||||
|
||||
/*
|
||||
* Zoom menu
|
||||
*/
|
||||
JMenuItem zoomMenu = new JMenu( "Zoom" );
|
||||
|
||||
double windowOffset = (double)event.getX() / (double)getWidth();
|
||||
|
||||
zoomMenu.add( new ZoomItem( frequency, windowOffset ) );
|
||||
|
||||
contextMenu.add( zoomMenu );
|
||||
|
||||
|
||||
if( contextMenu != null )
|
||||
{
|
||||
if( event.getComponent() == mOverlayPanel )
|
||||
@@ -741,6 +775,47 @@ public class SpectralDisplayPanel extends JPanel
|
||||
}
|
||||
}
|
||||
|
||||
public class ZoomItem extends JSlider
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private long mFrequency;
|
||||
private double mWindowOffset;
|
||||
|
||||
public ZoomItem( long frequency, double windowOffset )
|
||||
{
|
||||
super( NO_ZOOM, MAX_ZOOM, mZoom );
|
||||
|
||||
mFrequency = frequency;
|
||||
mWindowOffset = windowOffset;
|
||||
|
||||
Hashtable<Integer,JComponent> labels = new Hashtable<>();
|
||||
labels.put( new Integer( 0 ), new JLabel( "1x" ) );
|
||||
labels.put( new Integer( 1 ), new JLabel( "2x" ) );
|
||||
labels.put( new Integer( 2 ), new JLabel( "4x" ) );
|
||||
labels.put( new Integer( 3 ), new JLabel( "8x" ) );
|
||||
labels.put( new Integer( 4 ), new JLabel( "16x" ) );
|
||||
labels.put( new Integer( 5 ), new JLabel( "32x" ) );
|
||||
labels.put( new Integer( 6 ), new JLabel( "64x" ) );
|
||||
|
||||
setLabelTable( labels );
|
||||
|
||||
setMajorTickSpacing( 1 );
|
||||
setMinorTickSpacing( 1 );
|
||||
setPaintTicks( true );
|
||||
setPaintLabels( true );
|
||||
|
||||
this.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent e )
|
||||
{
|
||||
setZoom( getValue(), mFrequency, mWindowOffset );
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
public class ChannelDisplayItem extends JCheckBoxMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@@ -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
|
||||
@@ -51,40 +51,42 @@ public class SpectrumPanel extends JPanel
|
||||
SpectralDisplayAdjuster
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( SpectrumPanel.class );
|
||||
|
||||
/* 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 int mAveraging = 4;
|
||||
|
||||
private float mDBScale;
|
||||
|
||||
/**
|
||||
* Spectral Display 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";
|
||||
private static final String SPECTRUM_AVERAGING_SIZE = "spectrum_averaging_size";
|
||||
private static final int SPECTRUM_TRANSLUCENCY = 128;
|
||||
|
||||
private static final RenderingHints RENDERING_HINTS =
|
||||
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";
|
||||
|
||||
private Color mColorSpectrumBackground;
|
||||
private Color mColorSpectrumGradientTop;
|
||||
private Color mColorSpectrumGradientBottom;
|
||||
private Color mColorSpectrumLine;
|
||||
|
||||
//Defines the panel inset along the bottom for frequency display
|
||||
private float mSpectrumInset = 20.0f;
|
||||
|
||||
//Current DFT output bins in dB
|
||||
private float[] mDisplayFFTBins = new float[ 1 ];
|
||||
|
||||
//Averaging across multiple DFT result sets
|
||||
private int mAveraging = 4;
|
||||
|
||||
//Smoothing across bins in the same DFT result set
|
||||
private SmoothingFilter mSmoothingFilter = new GaussianSmoothingFilter();
|
||||
|
||||
//Reference dB value set according to the source sample size
|
||||
private float mDBScale;
|
||||
|
||||
private int mZoom = 0;
|
||||
private int mZoomBinOffset = 0;;
|
||||
|
||||
private ResourceManager mResourceManager;
|
||||
|
||||
@@ -109,104 +111,30 @@ public class SpectrumPanel extends JPanel
|
||||
mResourceManager = null;
|
||||
}
|
||||
|
||||
public void setSampleSize( double sampleSize )
|
||||
{
|
||||
assert( 2.0 <= sampleSize && sampleSize <= 32.0 );
|
||||
|
||||
mDBScale = (float)( 20.0 * Math.log10( 1 / Math.pow( 2.0, sampleSize - 1 ) ) );
|
||||
}
|
||||
|
||||
public void setAveraging( int size )
|
||||
{
|
||||
mAveraging = size;
|
||||
}
|
||||
|
||||
public int getAveraging()
|
||||
{
|
||||
return mAveraging;
|
||||
}
|
||||
|
||||
public void clearSpectrum()
|
||||
{
|
||||
mDisplayFFTBins = null;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void getSettings()
|
||||
{
|
||||
mColorSpectrumBackground =
|
||||
getColor( ColorSettingName.SPECTRUM_BACKGROUND );
|
||||
|
||||
mColorSpectrumGradientBottom =
|
||||
getColor( ColorSettingName.SPECTRUM_GRADIENT_BOTTOM );
|
||||
|
||||
mColorSpectrumGradientTop =
|
||||
getColor( ColorSettingName.SPECTRUM_GRADIENT_TOP );
|
||||
|
||||
mColorSpectrumLine = getColor( ColorSettingName.SPECTRUM_LINE );
|
||||
|
||||
mAveraging = 4;
|
||||
}
|
||||
|
||||
private Color getColor( ColorSettingName name )
|
||||
{
|
||||
ColorSetting setting =
|
||||
mResourceManager.getSettingsManager().getColorSetting( name );
|
||||
|
||||
return setting.getColor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting)setting;
|
||||
|
||||
switch( ( (ColorSetting) setting ).getColorSettingName() )
|
||||
{
|
||||
case SPECTRUM_BACKGROUND:
|
||||
mColorSpectrumBackground = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_GRADIENT_BOTTOM:
|
||||
mColorSpectrumGradientBottom = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_GRADIENT_TOP:
|
||||
mColorSpectrumGradientTop = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_LINE:
|
||||
mColorSpectrumLine = colorSetting.getColor();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* DFTResultsListener interface method - for receiving the processed data
|
||||
* DFTResultsListener interface for receiving the processed data
|
||||
* to display
|
||||
*/
|
||||
public void receive( float[] currentFFTBins )
|
||||
{
|
||||
//Prevent arrays of NaN values from being rendered. The first few
|
||||
//DFT result sets on startup will contain NaN values
|
||||
if( Float.isInfinite( currentFFTBins[ 0 ] ) || Float.isNaN( currentFFTBins[ 0 ] ) )
|
||||
{
|
||||
currentFFTBins = new float[ currentFFTBins.length ];
|
||||
}
|
||||
|
||||
//Construct and/or resize our fft results variables
|
||||
//Construct and/or resize our DFT results variables
|
||||
if( mDisplayFFTBins == null ||
|
||||
mDisplayFFTBins.length != currentFFTBins.length )
|
||||
{
|
||||
mDisplayFFTBins = currentFFTBins;
|
||||
}
|
||||
|
||||
//Smooth across the bins
|
||||
//Apply smoothing across the bins of the DFT results
|
||||
float[] smoothedBins = mSmoothingFilter.filter( currentFFTBins );
|
||||
|
||||
//Average bins over multiple frames
|
||||
//Apply averaging over multiple DFT output frames
|
||||
if( mAveraging > 1 )
|
||||
{
|
||||
float gain = 1.0f / (float)mAveraging;
|
||||
@@ -221,7 +149,6 @@ public class SpectrumPanel extends JPanel
|
||||
mDisplayFFTBins = smoothedBins;
|
||||
}
|
||||
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
@@ -270,17 +197,19 @@ public class SpectrumPanel extends JPanel
|
||||
|
||||
//Draw to the lower left
|
||||
spectrumShape.lineTo( 0, size.getHeight() - mSpectrumInset );
|
||||
|
||||
float[] bins = getBins();
|
||||
|
||||
//If we have FFT data to display ...
|
||||
if( mDisplayFFTBins != null )
|
||||
if( bins != null )
|
||||
{
|
||||
float insideHeight = size.height - mSpectrumInset;
|
||||
|
||||
float scalor = insideHeight / mDBScale;
|
||||
float scalor = insideHeight / -mDBScale;
|
||||
|
||||
float insideWidth = size.width;
|
||||
|
||||
int binCount = mDisplayFFTBins.length;
|
||||
int binCount = bins.length;
|
||||
|
||||
float binSize = insideWidth / ( binCount );
|
||||
|
||||
@@ -288,24 +217,17 @@ public class SpectrumPanel extends JPanel
|
||||
{
|
||||
float height;
|
||||
|
||||
if( mDisplayFFTBins[ x ] > 0 )
|
||||
height = bins[ x ] * scalor;
|
||||
|
||||
if( height > insideHeight )
|
||||
{
|
||||
height = insideHeight;
|
||||
}
|
||||
|
||||
if( height < 0 )
|
||||
{
|
||||
height = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
height = mDisplayFFTBins[ x ] * scalor;
|
||||
|
||||
if( height > insideHeight )
|
||||
{
|
||||
height = insideHeight;
|
||||
}
|
||||
|
||||
if( height < 0 )
|
||||
{
|
||||
height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
spectrumShape.lineTo( ( x * binSize ), height );
|
||||
}
|
||||
@@ -338,27 +260,188 @@ public class SpectrumPanel extends JPanel
|
||||
size.height - mSpectrumInset ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current zoom level
|
||||
*
|
||||
* 0 No Zoom
|
||||
* 1 2x Zoom
|
||||
* 2 4x Zoom
|
||||
* 3 8x Zoom
|
||||
* 4 16x Zoom
|
||||
* 5 32x Zoom
|
||||
*
|
||||
* @param zoom level, 0 - 5.
|
||||
*/
|
||||
public void setZoom( int zoom, int offset )
|
||||
{
|
||||
assert( 0 <= zoom && zoom <= 5 );
|
||||
|
||||
mZoom = zoom;
|
||||
|
||||
setZoomBinOffset( offset );
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset to define which subset of zoomed bins to display
|
||||
*
|
||||
* @param offset
|
||||
*/
|
||||
public void setZoomBinOffset( int offset )
|
||||
{
|
||||
mZoomBinOffset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the source sample size in bits which defines the lowest dB value to
|
||||
* display in the spectrum
|
||||
*
|
||||
* @param sampleSize in bits
|
||||
*/
|
||||
public void setSampleSize( double sampleSize )
|
||||
{
|
||||
assert( 2.0 <= sampleSize && sampleSize <= 32.0 );
|
||||
|
||||
mDBScale = (float)( 20.0 * Math.log10( Math.pow( 2.0, sampleSize - 1 ) ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the number of DFT result sets to average across
|
||||
*/
|
||||
public void setAveraging( int size )
|
||||
{
|
||||
mAveraging = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* DFT result sets averaging value
|
||||
*/
|
||||
public int getAveraging()
|
||||
{
|
||||
return mAveraging;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the spectral display
|
||||
*/
|
||||
public void clearSpectrum()
|
||||
{
|
||||
mDisplayFFTBins = null;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void getSettings()
|
||||
{
|
||||
mColorSpectrumBackground =
|
||||
getColor( ColorSettingName.SPECTRUM_BACKGROUND );
|
||||
|
||||
mColorSpectrumGradientBottom =
|
||||
getColor( ColorSettingName.SPECTRUM_GRADIENT_BOTTOM );
|
||||
|
||||
mColorSpectrumGradientTop =
|
||||
getColor( ColorSettingName.SPECTRUM_GRADIENT_TOP );
|
||||
|
||||
mColorSpectrumLine = getColor( ColorSettingName.SPECTRUM_LINE );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current setting for the named color setting
|
||||
*/
|
||||
private Color getColor( ColorSettingName name )
|
||||
{
|
||||
ColorSetting setting =
|
||||
mResourceManager.getSettingsManager().getColorSetting( name );
|
||||
|
||||
return setting.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener interface to receive setting change notifications
|
||||
*/
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting)setting;
|
||||
|
||||
switch( ( (ColorSetting) setting ).getColorSettingName() )
|
||||
{
|
||||
case SPECTRUM_BACKGROUND:
|
||||
mColorSpectrumBackground = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_GRADIENT_BOTTOM:
|
||||
mColorSpectrumGradientBottom = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_GRADIENT_TOP:
|
||||
mColorSpectrumGradientTop = colorSetting.getColor();
|
||||
break;
|
||||
case SPECTRUM_LINE:
|
||||
mColorSpectrumLine = colorSetting.getColor();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private float[] getBins()
|
||||
{
|
||||
if( mZoom == 0 )
|
||||
{
|
||||
return mDisplayFFTBins;
|
||||
}
|
||||
else
|
||||
{
|
||||
int zoom = (int)Math.pow( 2.0, mZoom );
|
||||
|
||||
int length = mDisplayFFTBins.length / zoom;
|
||||
|
||||
int offset = mZoomBinOffset;
|
||||
|
||||
if( ( offset + length ) > mDisplayFFTBins.length )
|
||||
{
|
||||
offset = mDisplayFFTBins.length - length;
|
||||
}
|
||||
|
||||
return Arrays.copyOfRange( mDisplayFFTBins, offset, offset + length );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void settingDeleted( Setting setting ) { /* not implemented */ }
|
||||
|
||||
/**
|
||||
* Returns the current inter-bin smoothing setting
|
||||
*/
|
||||
@Override
|
||||
public int getSmoothing()
|
||||
{
|
||||
return mSmoothingFilter.getPointSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bin smoothing value to define how wide the smoothing window is
|
||||
* when averaging across DFT bins
|
||||
*/
|
||||
@Override
|
||||
public void setSmoothing( int smoothing )
|
||||
{
|
||||
mSmoothingFilter.setPointSize( smoothing );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current smoothing filter type
|
||||
*/
|
||||
@Override
|
||||
public SmoothingType getSmoothingType()
|
||||
{
|
||||
return mSmoothingFilter.getSmoothingType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the smoothing filter type
|
||||
*/
|
||||
@Override
|
||||
public void setSmoothingType( SmoothingType type )
|
||||
{
|
||||
|
||||
@@ -60,7 +60,7 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
private static final String PAUSED = "PAUSED";
|
||||
|
||||
private byte[] mPixels;
|
||||
private int mFFTSize = 1024;
|
||||
private int mDFTSize = 4096;
|
||||
private int mImageHeight = 700;
|
||||
private MemoryImageSource mMemoryImageSource;
|
||||
private ColorModel mColorModel = WaterfallColorModel.getDefaultColorModel();
|
||||
@@ -71,6 +71,8 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
private boolean mCursorVisible = false;
|
||||
private long mCursorFrequency = 0;
|
||||
private AtomicBoolean mPaused = new AtomicBoolean();
|
||||
private int mZoom = 0;
|
||||
private int mDFTZoomOffset = 0;
|
||||
|
||||
private ResourceManager mResourceManager;
|
||||
|
||||
@@ -93,14 +95,14 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
|
||||
private void reset()
|
||||
{
|
||||
mPixels = new byte[ mFFTSize * mImageHeight ];
|
||||
mPixels = new byte[ mDFTSize * mImageHeight ];
|
||||
|
||||
mMemoryImageSource = new MemoryImageSource( mFFTSize,
|
||||
mMemoryImageSource = new MemoryImageSource( mDFTSize,
|
||||
mImageHeight,
|
||||
mColorModel,
|
||||
mPixels,
|
||||
0,
|
||||
mFFTSize );
|
||||
mDFTSize );
|
||||
|
||||
mMemoryImageSource.setAnimated( true );
|
||||
|
||||
@@ -122,6 +124,25 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
return mPaused.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current zoom level (2^zoom)
|
||||
*
|
||||
* 0 No Zoom
|
||||
* 1 2x Zoom
|
||||
* 2 4x Zoom
|
||||
* 3 8x Zoom
|
||||
* 4 16x Zoom
|
||||
* 5 32x Zoom
|
||||
*
|
||||
* @param zoom level, 0 - 5.
|
||||
* @param offset into the DFT bins for display
|
||||
*/
|
||||
public void setZoom( int zoom, int offset )
|
||||
{
|
||||
mZoom = zoom;
|
||||
mDFTZoomOffset = offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a named color setting from the settings manager. If the setting
|
||||
* doesn't exist, creates the setting using the defaultColor
|
||||
@@ -171,10 +192,31 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
private int getPixelOffset( int multiplier )
|
||||
{
|
||||
if( mZoom != 0 )
|
||||
{
|
||||
return -(int)( (double)getWidth() * multiplier /
|
||||
(double)mDFTSize *
|
||||
(double)( mDFTZoomOffset ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void paintComponent( Graphics g )
|
||||
{
|
||||
g.drawImage( mWaterfallImage, 0, 0, getWidth(), mImageHeight, this );
|
||||
int multiplier = (int)Math.pow( 2.0, mZoom );
|
||||
|
||||
int offset = getPixelOffset( multiplier );
|
||||
|
||||
g.drawImage( mWaterfallImage,
|
||||
offset,
|
||||
0,
|
||||
getWidth() * multiplier,
|
||||
mImageHeight,
|
||||
this );
|
||||
|
||||
Graphics2D graphics = (Graphics2D) g;
|
||||
|
||||
@@ -194,6 +236,11 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
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() )
|
||||
@@ -208,16 +255,16 @@ 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( mDFTSize != update.length )
|
||||
{
|
||||
mFFTSize = update.length;
|
||||
mDFTSize = update.length;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
//Move the pixels down a row to make room for the new results
|
||||
System.arraycopy( mPixels, 0,
|
||||
mPixels, mFFTSize, mPixels.length - mFFTSize );
|
||||
mPixels, mDFTSize, mPixels.length - mDFTSize );
|
||||
|
||||
/**
|
||||
* Find the average value and scale the display to it
|
||||
@@ -260,7 +307,7 @@ public class WaterfallPanel extends JPanel implements DFTResultsListener,
|
||||
{
|
||||
if( mMemoryImageSource != null )
|
||||
{
|
||||
mMemoryImageSource.newPixels( mPixels, mColorModel, 0, mFFTSize );
|
||||
mMemoryImageSource.newPixels( mPixels, mColorModel, 0, mDFTSize );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
@@ -78,7 +78,8 @@ public class ComplexDecibelConverter extends DFTResultsConverter
|
||||
{
|
||||
int halfResults = results.length / 2;
|
||||
|
||||
float dftScalor = 1.0f / (float)( halfResults ^ 2 );
|
||||
// float dftBinSizeScalor = 1.0f / (float)Math.pow( halfResults, 2.0 );
|
||||
float dftBinSizeScalor = 1.0f / (float)halfResults;
|
||||
|
||||
float[] processed = new float[ halfResults ];
|
||||
|
||||
@@ -91,7 +92,7 @@ public class ComplexDecibelConverter extends DFTResultsConverter
|
||||
//Convert the scaled value to decibels.
|
||||
float decibels = 10.0f * (float)Math.log10(
|
||||
( ( results[ x ] * results[ x ] ) +
|
||||
( results[ x + 1 ] * results[ x + 1 ] ) ) * dftScalor );
|
||||
( results[ x + 1 ] * results[ x + 1 ] ) ) * dftBinSizeScalor );
|
||||
|
||||
// We have to swap the upper and lower halves of the JTransforms
|
||||
// DFT results for correct display
|
||||
|
||||
@@ -21,7 +21,7 @@ public class RealDecibelConverter extends DFTResultsConverter
|
||||
@Override
|
||||
public void receive( float[] results )
|
||||
{
|
||||
float dftScalor = 1.0f / (float)( results.length ^ 2 );
|
||||
float dftBinSizeScalor = 1.0f / (float)results.length;
|
||||
|
||||
float[] processed = new float[ results.length / 4 ];
|
||||
|
||||
@@ -33,7 +33,7 @@ public class RealDecibelConverter extends DFTResultsConverter
|
||||
|
||||
processed[ x ] = 20.0f * (float)Math.log10(
|
||||
( ( results[ index ] * results[ index ] ) +
|
||||
( results[ index + 1 ] * results[ index + 1 ] ) ) * dftScalor );
|
||||
( results[ index + 1 ] * results[ index + 1 ] ) ) * dftBinSizeScalor );
|
||||
}
|
||||
|
||||
dispatch( processed );
|
||||
|
||||
@@ -5,24 +5,24 @@ import java.awt.event.ActionListener;
|
||||
|
||||
import javax.swing.JCheckBoxMenuItem;
|
||||
|
||||
import spectrum.DFTProcessor;
|
||||
import spectrum.FFTWidth;
|
||||
import spectrum.DFTSize;
|
||||
import spectrum.IDFTWidthChangeProcessor;
|
||||
|
||||
public class FFTWidthItem extends JCheckBoxMenuItem
|
||||
public class DFTSizeItem extends JCheckBoxMenuItem
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private DFTProcessor mDFTProcessor;
|
||||
private FFTWidth mFFTWidth;
|
||||
private IDFTWidthChangeProcessor mDFTProcessor;
|
||||
private DFTSize mDFTSize;
|
||||
|
||||
public FFTWidthItem( DFTProcessor processor, FFTWidth width )
|
||||
public DFTSizeItem( IDFTWidthChangeProcessor processor, DFTSize size )
|
||||
{
|
||||
super( width.getLabel() );
|
||||
super( size.getLabel() );
|
||||
|
||||
mDFTProcessor = processor;
|
||||
mFFTWidth = width;
|
||||
mDFTSize = size;
|
||||
|
||||
if( processor.getFFTWidth() == mFFTWidth )
|
||||
if( processor.getDFTSize() == mDFTSize )
|
||||
{
|
||||
setSelected( true );
|
||||
}
|
||||
@@ -32,7 +32,7 @@ public class FFTWidthItem extends JCheckBoxMenuItem
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent arg0 )
|
||||
{
|
||||
mDFTProcessor.setFFTSize( mFFTWidth );
|
||||
mDFTProcessor.setDFTSize( mDFTSize );
|
||||
}
|
||||
} );
|
||||
}
|
||||
Reference in New Issue
Block a user