mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
ordering to each channel configuration to provide the user with greater control over initial application startup configuration. Adds new dialog window that displays on startup that allows the user to invoke/cancel auto-start, or simply allow the timer to expire whereby the channels will be auto-started. Updates channel table model to include an auto-start column and relabels the 'Enabled' column to read 'Playing'. Updates channel editor to change the 'Enable/Disable' button to read 'Start/Stop'. Changes the 'enabled' property of the channel configuration to indicate the current 'processing' status - isProcessing(). Channel's XML configuration re-purposes the 'enabled' property to indicate auto-start and adds a new (optional) auto-start 'order' attribute.
490 lines
13 KiB
Java
490 lines
13 KiB
Java
package io.github.dsheirer.spectrum;
|
|
|
|
import io.github.dsheirer.controller.channel.Channel;
|
|
import io.github.dsheirer.controller.channel.ChannelEvent;
|
|
import io.github.dsheirer.controller.channel.ChannelEventListener;
|
|
import io.github.dsheirer.controller.channel.ChannelProcessingManager;
|
|
import io.github.dsheirer.dsp.filter.Filters;
|
|
import io.github.dsheirer.dsp.filter.Window.WindowType;
|
|
import io.github.dsheirer.dsp.filter.halfband.real.HalfBandFilter_RB_RB;
|
|
import io.github.dsheirer.dsp.filter.smoothing.SmoothingFilter.SmoothingType;
|
|
import io.github.dsheirer.module.ProcessingChain;
|
|
import io.github.dsheirer.sample.Listener;
|
|
import io.github.dsheirer.sample.SampleType;
|
|
import io.github.dsheirer.sample.complex.ComplexBuffer;
|
|
import io.github.dsheirer.sample.real.RealBuffer;
|
|
import io.github.dsheirer.settings.ColorSetting.ColorSettingName;
|
|
import io.github.dsheirer.settings.ColorSettingMenuItem;
|
|
import io.github.dsheirer.settings.Setting;
|
|
import io.github.dsheirer.settings.SettingChangeListener;
|
|
import io.github.dsheirer.settings.SettingsManager;
|
|
import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent;
|
|
import io.github.dsheirer.source.tuner.frequency.FrequencyChangeEvent.Event;
|
|
import io.github.dsheirer.spectrum.converter.DFTResultsConverter;
|
|
import io.github.dsheirer.spectrum.converter.RealDecibelConverter;
|
|
import io.github.dsheirer.spectrum.menu.AveragingItem;
|
|
import io.github.dsheirer.spectrum.menu.DFTSizeItem;
|
|
import io.github.dsheirer.spectrum.menu.FFTWindowTypeItem;
|
|
import io.github.dsheirer.spectrum.menu.FrameRateItem;
|
|
import io.github.dsheirer.spectrum.menu.SmoothingItem;
|
|
import io.github.dsheirer.spectrum.menu.SmoothingTypeItem;
|
|
import net.miginfocom.swing.MigLayout;
|
|
|
|
import javax.swing.JLayeredPane;
|
|
import javax.swing.JMenu;
|
|
import javax.swing.JPanel;
|
|
import javax.swing.JPopupMenu;
|
|
import javax.swing.JSeparator;
|
|
import javax.swing.SwingUtilities;
|
|
import java.awt.Component;
|
|
import java.awt.event.ComponentEvent;
|
|
import java.awt.event.ComponentListener;
|
|
import java.awt.event.MouseEvent;
|
|
import java.awt.event.MouseListener;
|
|
import java.awt.event.MouseMotionListener;
|
|
import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
public class ChannelSpectrumPanel extends JPanel
|
|
implements ChannelEventListener,
|
|
Listener<RealBuffer>,
|
|
SettingChangeListener,
|
|
SpectralDisplayAdjuster
|
|
{
|
|
private static final long serialVersionUID = 1L;
|
|
|
|
private DFTProcessor mDFTProcessor = new DFTProcessor( SampleType.REAL );
|
|
private DFTResultsConverter mDFTConverter = new RealDecibelConverter();
|
|
private JLayeredPane mLayeredPane;
|
|
private SpectrumPanel mSpectrumPanel;
|
|
private ChannelOverlayPanel mOverlayPanel;
|
|
|
|
private Channel mCurrentChannel;
|
|
|
|
private int mSampleBufferSize = 2400;
|
|
|
|
private HalfBandFilter_RB_RB mDecimatingFilter = new HalfBandFilter_RB_RB(
|
|
Filters.FIR_HALF_BAND_31T_ONE_EIGHTH_FCO.getCoefficients(), 1.0f, true );
|
|
|
|
private AtomicBoolean mEnabled = new AtomicBoolean();
|
|
|
|
private SettingsManager mSettingsManager;
|
|
private ChannelProcessingManager mChannelProcessingManager;
|
|
|
|
public ChannelSpectrumPanel( SettingsManager settingsManager,
|
|
ChannelProcessingManager channelProcessingManager )
|
|
{
|
|
mSettingsManager = settingsManager;
|
|
mChannelProcessingManager = channelProcessingManager;
|
|
|
|
if( mSettingsManager != null )
|
|
{
|
|
mSettingsManager.addListener( this );
|
|
}
|
|
|
|
mSpectrumPanel = new SpectrumPanel( mSettingsManager );
|
|
mSpectrumPanel.setAveraging( 1 );
|
|
|
|
mOverlayPanel = new ChannelOverlayPanel( mSettingsManager );
|
|
mDFTProcessor.addConverter( mDFTConverter );
|
|
mDFTConverter.addListener( mSpectrumPanel );
|
|
|
|
/* Set the DFTProcessor to the decimated 24kHz sample rate */
|
|
mDFTProcessor.frequencyChanged(
|
|
new FrequencyChangeEvent( Event.NOTIFICATION_SAMPLE_RATE_CHANGE, 24000 ) );
|
|
|
|
initGui();
|
|
}
|
|
|
|
public void dispose()
|
|
{
|
|
setEnabled( false );
|
|
|
|
mDFTProcessor.dispose();
|
|
|
|
if( mSettingsManager != null )
|
|
{
|
|
mSettingsManager.removeListener( this );
|
|
}
|
|
|
|
mSettingsManager = null;
|
|
mCurrentChannel = null;
|
|
mDFTProcessor = null;
|
|
mSpectrumPanel = null;
|
|
}
|
|
|
|
public void setFrameRate( int framesPerSecond )
|
|
{
|
|
mSampleBufferSize = (int)( 48000 / framesPerSecond );
|
|
|
|
mDFTProcessor.setFrameRate( framesPerSecond );
|
|
}
|
|
|
|
private void initGui()
|
|
{
|
|
setLayout( new MigLayout( "insets 0 0 0 0 ",
|
|
"[grow,fill]",
|
|
"[grow,fill]") );
|
|
|
|
mLayeredPane = new JLayeredPane();
|
|
mLayeredPane.addComponentListener( new ResizeListener() );
|
|
|
|
MouseEventProcessor mouser = new MouseEventProcessor();
|
|
|
|
mOverlayPanel.addMouseListener( mouser );
|
|
mOverlayPanel.addMouseMotionListener( mouser );
|
|
|
|
mLayeredPane.add( mSpectrumPanel, new Integer( 0 ), 0 );
|
|
mLayeredPane.add( mOverlayPanel, new Integer( 1 ), 0 );
|
|
|
|
add( mLayeredPane );
|
|
}
|
|
|
|
public void setEnabled( boolean enabled )
|
|
{
|
|
if( enabled && mEnabled.compareAndSet( false, true ) )
|
|
{
|
|
start();
|
|
}
|
|
else if( !enabled && mEnabled.compareAndSet( true, false ) )
|
|
{
|
|
stop();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings( "incomplete-switch" )
|
|
public void channelChanged( ChannelEvent event )
|
|
{
|
|
switch( event.getEvent() )
|
|
{
|
|
case NOTIFICATION_SELECTION_CHANGE:
|
|
//ChannelSelectionManager ensures that only 1 channel can be
|
|
//selected and any previously selected channel will be first
|
|
//deselected before we get a new selection event
|
|
if( event.getChannel().isSelected() )
|
|
{
|
|
if( mCurrentChannel != null )
|
|
{
|
|
stop();
|
|
mCurrentChannel = null;
|
|
}
|
|
|
|
mCurrentChannel = event.getChannel();
|
|
|
|
if( mEnabled.get() )
|
|
{
|
|
start();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
stop();
|
|
mCurrentChannel = null;
|
|
}
|
|
break;
|
|
case NOTIFICATION_PROCESSING_STOP:
|
|
if( event.getChannel() == mCurrentChannel )
|
|
{
|
|
if( mEnabled.get() )
|
|
{
|
|
stop();
|
|
}
|
|
|
|
mCurrentChannel = null;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void start()
|
|
{
|
|
if( mEnabled.get() && mCurrentChannel != null && mCurrentChannel.isProcessing() )
|
|
{
|
|
ProcessingChain processingChain = mChannelProcessingManager
|
|
.getProcessingChain( mCurrentChannel );
|
|
|
|
if( processingChain != null )
|
|
{
|
|
processingChain.addRealBufferListener( this );
|
|
|
|
mDFTProcessor.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void stop()
|
|
{
|
|
if( mCurrentChannel != null && mCurrentChannel.isProcessing() )
|
|
{
|
|
ProcessingChain processingChain = mChannelProcessingManager
|
|
.getProcessingChain( mCurrentChannel );
|
|
|
|
if( processingChain != null )
|
|
{
|
|
processingChain.removeRealBufferListener( this );
|
|
}
|
|
}
|
|
|
|
mDFTProcessor.stop();
|
|
|
|
mSpectrumPanel.clearSpectrum();
|
|
}
|
|
|
|
@Override
|
|
public void settingChanged( Setting setting )
|
|
{
|
|
if( mSpectrumPanel != null )
|
|
{
|
|
mSpectrumPanel.settingChanged( setting );
|
|
}
|
|
if( mOverlayPanel != null )
|
|
{
|
|
mOverlayPanel.settingChanged( setting );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void settingDeleted( Setting setting )
|
|
{
|
|
if( mSpectrumPanel != null )
|
|
{
|
|
mSpectrumPanel.settingDeleted( setting );
|
|
}
|
|
|
|
if( mOverlayPanel != null )
|
|
{
|
|
mOverlayPanel.settingDeleted( setting );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void receive( RealBuffer buffer )
|
|
{
|
|
RealBuffer decimated = mDecimatingFilter.filter( buffer );
|
|
|
|
//Hack: we're placing real samples in a complex buffer that the DFT
|
|
//processor is expecting.
|
|
mDFTProcessor.receive( new ComplexBuffer( decimated.getSamples() ) );
|
|
}
|
|
|
|
/**
|
|
* Monitors the sizing of the layered pane and resizes the spectrum and
|
|
* channel panels whenever the layered pane is resized
|
|
*/
|
|
public class ResizeListener implements ComponentListener
|
|
{
|
|
@Override
|
|
public void componentResized( ComponentEvent e )
|
|
{
|
|
Component c = e.getComponent();
|
|
|
|
mSpectrumPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
|
|
mOverlayPanel.setBounds( 0, 0, c.getWidth(), c.getHeight() );
|
|
}
|
|
|
|
@Override
|
|
public void componentHidden( ComponentEvent arg0 ) {}
|
|
@Override
|
|
public void componentMoved( ComponentEvent arg0 ) {}
|
|
@Override
|
|
public void componentShown( ComponentEvent arg0 ) {}
|
|
}
|
|
|
|
/**
|
|
* Mouse event handler for the channel panel.
|
|
*/
|
|
public class MouseEventProcessor implements MouseMotionListener, MouseListener
|
|
{
|
|
@Override
|
|
public void mouseMoved( MouseEvent event )
|
|
{
|
|
update( event );
|
|
}
|
|
@Override
|
|
public void mouseDragged( MouseEvent event )
|
|
{
|
|
update( event );
|
|
}
|
|
|
|
private void update( MouseEvent event )
|
|
{
|
|
if( event.getComponent() == mOverlayPanel )
|
|
{
|
|
mOverlayPanel.setCursorLocation( event.getPoint() );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mouseEntered( MouseEvent e )
|
|
{
|
|
if( e.getComponent() == mOverlayPanel )
|
|
{
|
|
mOverlayPanel.setCursorVisible( true );
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mouseExited( MouseEvent e )
|
|
{
|
|
mOverlayPanel.setCursorVisible( false );
|
|
}
|
|
|
|
/**
|
|
* Displays the context menu.
|
|
*/
|
|
@Override
|
|
public void mouseClicked( MouseEvent event )
|
|
{
|
|
if( SwingUtilities.isRightMouseButton( event ) )
|
|
{
|
|
JPopupMenu contextMenu = new JPopupMenu();
|
|
|
|
/**
|
|
* Color Menus
|
|
*/
|
|
JMenu colorMenu = new JMenu( "Color" );
|
|
|
|
colorMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
|
ColorSettingName.SPECTRUM_CURSOR ) );
|
|
|
|
colorMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
|
ColorSettingName.SPECTRUM_LINE ) );
|
|
|
|
colorMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
|
ColorSettingName.SPECTRUM_BACKGROUND ) );
|
|
|
|
colorMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
|
ColorSettingName.SPECTRUM_GRADIENT_BOTTOM ) );
|
|
|
|
colorMenu.add( new ColorSettingMenuItem( mSettingsManager,
|
|
ColorSettingName.SPECTRUM_GRADIENT_TOP ) );
|
|
|
|
contextMenu.add( colorMenu );
|
|
|
|
/**
|
|
* Display items: fft and frame rate
|
|
*/
|
|
JMenu displayMenu = new JMenu( "Display" );
|
|
contextMenu.add( displayMenu );
|
|
|
|
/**
|
|
* Averaging menu
|
|
*/
|
|
JMenu averagingMenu = new JMenu( "Averaging" );
|
|
averagingMenu.add(
|
|
new AveragingItem( ChannelSpectrumPanel.this, 2 ) );
|
|
displayMenu.add( averagingMenu );
|
|
|
|
/**
|
|
* FFT width
|
|
*/
|
|
JMenu fftWidthMenu = new JMenu( "FFT Width" );
|
|
displayMenu.add( fftWidthMenu );
|
|
|
|
for( DFTSize width: DFTSize.values() )
|
|
{
|
|
fftWidthMenu.add( new DFTSizeItem( mDFTProcessor, width ) );
|
|
}
|
|
|
|
/**
|
|
* DFT Processor Frame Rate
|
|
*/
|
|
JMenu frameRateMenu = new JMenu( "Frame Rate" );
|
|
displayMenu.add( frameRateMenu );
|
|
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 14 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 16 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 18 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 20 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 25 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 30 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 40 ) );
|
|
frameRateMenu.add( new FrameRateItem( mDFTProcessor, 50 ) );
|
|
|
|
/**
|
|
* FFT Window Type
|
|
*/
|
|
JMenu fftWindowType = new JMenu( "Window Type" );
|
|
displayMenu.add( fftWindowType );
|
|
|
|
for( WindowType type: WindowType.values() )
|
|
{
|
|
fftWindowType.add(
|
|
new FFTWindowTypeItem( mDFTProcessor, type ) );
|
|
}
|
|
|
|
/**
|
|
* Smoothing menu
|
|
*/
|
|
JMenu smoothingMenu = new JMenu( "Smoothing" );
|
|
|
|
if( mSpectrumPanel.getSmoothingType() != SmoothingType.NONE )
|
|
{
|
|
smoothingMenu.add( new SmoothingItem( ChannelSpectrumPanel.this, 5 ) );
|
|
smoothingMenu.add( new JSeparator() );
|
|
}
|
|
|
|
smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.GAUSSIAN ) );
|
|
smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.TRIANGLE ) );
|
|
smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.RECTANGLE ) );
|
|
smoothingMenu.add( new SmoothingTypeItem( ChannelSpectrumPanel.this, SmoothingType.NONE ) );
|
|
|
|
displayMenu.add( smoothingMenu );
|
|
|
|
if( contextMenu != null )
|
|
{
|
|
contextMenu.show( mOverlayPanel,
|
|
event.getX(),
|
|
event.getY() );
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void mousePressed( MouseEvent e ) {}
|
|
@Override
|
|
public void mouseReleased( MouseEvent e ) {}
|
|
}
|
|
|
|
@Override
|
|
public int getAveraging()
|
|
{
|
|
return mSpectrumPanel.getAveraging();
|
|
}
|
|
|
|
@Override
|
|
public void setAveraging( int averaging )
|
|
{
|
|
mSpectrumPanel.setAveraging( averaging );
|
|
}
|
|
|
|
public void setSampleSize( double sampleSize )
|
|
{
|
|
mSpectrumPanel.setSampleSize( sampleSize );
|
|
}
|
|
|
|
@Override
|
|
public int getSmoothing()
|
|
{
|
|
return mSpectrumPanel.getSmoothing();
|
|
}
|
|
|
|
@Override
|
|
public void setSmoothing( int smoothing )
|
|
{
|
|
mSpectrumPanel.setSmoothing( smoothing );
|
|
}
|
|
|
|
@Override
|
|
public SmoothingType getSmoothingType()
|
|
{
|
|
return mSpectrumPanel.getSmoothingType();
|
|
}
|
|
|
|
@Override
|
|
public void setSmoothingType( SmoothingType type )
|
|
{
|
|
mSpectrumPanel.setSmoothingType( type );
|
|
}
|
|
}
|