mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
Updates pull request from Roman Tsypuk to convert the build system from
ant to gradle. Removes linux and windows application launch scripts and produces an executable jar with all resources included.
This commit is contained in:
431
src/main/java/io/github/dsheirer/spectrum/WaterfallPanel.java
Normal file
431
src/main/java/io/github/dsheirer/spectrum/WaterfallPanel.java
Normal file
@@ -0,0 +1,431 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014,2015 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package io.github.dsheirer.spectrum;
|
||||
|
||||
import io.github.dsheirer.settings.ColorSetting;
|
||||
import io.github.dsheirer.settings.ColorSetting.ColorSettingName;
|
||||
import io.github.dsheirer.settings.Setting;
|
||||
import io.github.dsheirer.settings.SettingChangeListener;
|
||||
import io.github.dsheirer.settings.SettingsManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.awt.geom.Line2D;
|
||||
import java.awt.image.ColorModel;
|
||||
import java.awt.image.MemoryImageSource;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
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 DecimalFormat CURSOR_FORMAT = new DecimalFormat( "0.00000" );
|
||||
private static final String PAUSED = "PAUSED";
|
||||
|
||||
private byte[] mPixels;
|
||||
private byte[] mPausedPixels;
|
||||
private int mDFTSize = 4096;
|
||||
private int mImageHeight = 700;
|
||||
private MemoryImageSource mMemoryImageSource;
|
||||
private ColorModel mColorModel = WaterfallColorModel.getDefaultColorModel();
|
||||
private Color mColorSpectrumCursor;
|
||||
private Image mWaterfallImage;
|
||||
|
||||
private Point mCursorLocation = new Point( 0, 0 );
|
||||
private boolean mCursorVisible = false;
|
||||
private long mCursorFrequency = 0;
|
||||
private boolean mPaused = false;
|
||||
private int mZoom = 0;
|
||||
private int mDFTZoomWindowOffset = 0;
|
||||
|
||||
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();
|
||||
|
||||
mSettingsManager = settingsManager;
|
||||
|
||||
mSettingsManager.addListener( this );
|
||||
|
||||
mColorSpectrumCursor = getColor( ColorSettingName.SPECTRUM_CURSOR );
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares this instance for disposal
|
||||
*/
|
||||
public void dispose()
|
||||
{
|
||||
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 ];
|
||||
|
||||
mMemoryImageSource = new MemoryImageSource( mDFTSize,
|
||||
mImageHeight,
|
||||
mColorModel,
|
||||
mPixels,
|
||||
0,
|
||||
mDFTSize );
|
||||
|
||||
mMemoryImageSource.setAnimated( true );
|
||||
|
||||
mWaterfallImage = createImage( mMemoryImageSource );
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Pausable interface - pauses updates to the waterfall
|
||||
*/
|
||||
public void setPaused( boolean paused )
|
||||
{
|
||||
if( paused )
|
||||
{
|
||||
mPausedPixels = mPixels.clone();
|
||||
}
|
||||
|
||||
mPaused = paused;
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current pause state
|
||||
* @return true if paused, false otherwise
|
||||
*/
|
||||
public boolean isPaused()
|
||||
{
|
||||
return mPaused;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 - 6.
|
||||
* @param offset into the DFT bins for display
|
||||
*/
|
||||
public void setZoom( int zoom )
|
||||
{
|
||||
mZoom = zoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = mSettingsManager.getColorSetting( name );
|
||||
|
||||
return setting.getColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Monitors for setting changes. Colors can be changed by external actions
|
||||
* and will automatically update in this class
|
||||
*/
|
||||
@Override
|
||||
public void settingChanged( Setting setting )
|
||||
{
|
||||
if( setting instanceof ColorSetting )
|
||||
{
|
||||
ColorSetting colorSetting = (ColorSetting)setting;
|
||||
|
||||
if( ( (ColorSetting) setting ).getColorSettingName() ==
|
||||
ColorSettingName.SPECTRUM_CURSOR )
|
||||
{
|
||||
mColorSpectrumCursor = colorSetting.getColor();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
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 double getPixelOffset( int multiplier )
|
||||
{
|
||||
double offset = 0;
|
||||
|
||||
if( mZoom != 0 )
|
||||
{
|
||||
double binPixelWidth = getBinPixelWidth( multiplier );
|
||||
|
||||
offset = -binPixelWidth * (double)( mDFTZoomWindowOffset );
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
private double getBinPixelWidth( int multiplier )
|
||||
{
|
||||
return ( (double)getWidth() * (double)multiplier ) / (double)mDFTSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the screen at each refresh
|
||||
*/
|
||||
public void paintComponent( Graphics g )
|
||||
{
|
||||
super.paintComponent( g );
|
||||
|
||||
int multiplier = getZoomMultiplier();
|
||||
|
||||
double binPixelWidth = getBinPixelWidth( multiplier );
|
||||
|
||||
int offset = (int)( getPixelOffset( multiplier ) - binPixelWidth );
|
||||
|
||||
g.drawImage( mWaterfallImage,
|
||||
offset,
|
||||
0,
|
||||
( getWidth() * multiplier ) + (int)binPixelWidth,
|
||||
mImageHeight,
|
||||
this );
|
||||
|
||||
Graphics2D graphics = (Graphics2D) g;
|
||||
|
||||
graphics.setColor( mColorSpectrumCursor );
|
||||
|
||||
if( mCursorVisible )
|
||||
{
|
||||
|
||||
|
||||
graphics.draw( new Line2D.Float( mCursorLocation.x,
|
||||
0,
|
||||
mCursorLocation.x,
|
||||
(float)(getSize().getHeight() ) ) );
|
||||
|
||||
String frequency = CURSOR_FORMAT.format( mCursorFrequency / 1000000.0D );
|
||||
|
||||
graphics.drawString( frequency ,
|
||||
mCursorLocation.x + 5,
|
||||
mCursorLocation.y );
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
//If our FFT size changes, reset our pixel map and image source
|
||||
if( mDFTSize != update.length )
|
||||
{
|
||||
mDFTSize = update.length;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
//Move the pixels down a row to make room for the new results
|
||||
System.arraycopy( mPixels, 0,
|
||||
mPixels, mDFTSize, mPixels.length - mDFTSize );
|
||||
|
||||
/**
|
||||
* Find the average value and scale the display to it
|
||||
*/
|
||||
double sum = 0.0d;
|
||||
|
||||
for( int x = 0; x < update.length - 1; x++ )
|
||||
{
|
||||
sum += update[ x ];
|
||||
}
|
||||
|
||||
float average = (float)( sum / (double)update.length - 1 );
|
||||
|
||||
float scale = 256.0f / average;
|
||||
|
||||
for( int x = 0; x < update.length - 1; x++ )
|
||||
{
|
||||
float value = ( average - update[ x ] ) * scale;
|
||||
|
||||
if( value < 0 )
|
||||
{
|
||||
mPixels[ x ] = 0;
|
||||
}
|
||||
else if( value > 255 )
|
||||
{
|
||||
mPixels[ x ] = (byte)255;
|
||||
}
|
||||
else
|
||||
{
|
||||
mPixels[ x ] = (byte)value;
|
||||
}
|
||||
}
|
||||
|
||||
//Task the swing event thread to update the display
|
||||
EventQueue.invokeLater( new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if( mMemoryImageSource != null )
|
||||
{
|
||||
if( mPaused )
|
||||
{
|
||||
mMemoryImageSource.newPixels( mPausedPixels, mColorModel, 0, mDFTSize );
|
||||
}
|
||||
else
|
||||
{
|
||||
mMemoryImageSource.newPixels( mPixels, mColorModel, 0, mDFTSize );
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user