From 104c4f72be0bd7975322b42a48f9c44608f61acf Mon Sep 17 00:00:00 2001 From: Denny Date: Sat, 14 Jul 2018 07:45:42 -0400 Subject: [PATCH] Resolves #170 adding support for Airspy Mini tuner's 3 and 6 MHz sample rates. Resolves issue in PolyphaseChannelManager responding to sample rate changes in the tuner. The manager now recreates the channelizer when needed if the tuner's sample rate has changed since the last time the channelizer was used. Updates the Tuner selection view to reflect tuner lock state. Updates the tuner editors to enable/disable frequency controller and sample rate controls according to the tuner's lock state. --- .../AbstractComplexPolyphaseChannelizer.java | 3 +- .../filter/channelizer/ChannelCalculator.java | 51 +--- .../ComplexPolyphaseChannelizerM2.java | 7 +- .../channelizer/PolyphaseChannelManager.java | 55 ++-- .../gui/control/JFrequencyControl.java | 24 ++ .../github/dsheirer/source/tuner/Tuner.java | 4 + .../dsheirer/source/tuner/TunerEvent.java | 57 ++-- .../dsheirer/source/tuner/TunerModel.java | 11 +- .../dsheirer/source/tuner/TunerViewPanel.java | 278 ++++++++++-------- .../tuner/airspy/AirspyTunerController.java | 21 +- .../tuner/airspy/AirspyTunerEditor.java | 45 +-- .../TunerConfigurationEditor.java | 8 - .../tuner/fcd/proV1/FCD1TunerEditor.java | 6 - .../tuner/fcd/proplusV2/FCD2TunerEditor.java | 6 - .../tuner/frequency/FrequencyController.java | 37 ++- .../tuner/hackrf/HackRFTunerEditor.java | 37 ++- .../source/tuner/rtl/e4k/E4KTunerEditor.java | 37 ++- .../tuner/rtl/r820t/R820TTunerEditor.java | 37 ++- 18 files changed, 376 insertions(+), 348 deletions(-) diff --git a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java index 102f145b..09f6d34a 100644 --- a/src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java +++ b/src/main/java/io/github/dsheirer/dsp/filter/channelizer/AbstractComplexPolyphaseChannelizer.java @@ -68,9 +68,10 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener 0.5) + //Channel calculator is always in sync with the tuner's current sample rate + double tunerSampleRate = mChannelCalculator.getSampleRate(); + + //If the channelizer is not setup, or setup to the wrong sample rate, recreate it + if(mPolyphaseChannelizer == null || Math.abs(mPolyphaseChannelizer.getSampleRate() - tunerSampleRate) > 0.5) { + if(mPolyphaseChannelizer != null && mPolyphaseChannelizer.getRegisteredChannelCount() > 0) + { + throw new IllegalStateException("Polyphase Channelizer cannot be changed to a new sample rate while " + + "channels are currently sourced. Ensure you remove all tuner channels before changing tuner " + + "sample rate. Current channel count:" + + (mPolyphaseChannelizer != null ? mPolyphaseChannelizer.getRegisteredChannelCount() : "0")); + } + try { - if(mPolyphaseChannelizer == null) - { - mPolyphaseChannelizer = new ComplexPolyphaseChannelizerM2(sampleRate, - POLYPHASE_CHANNELIZER_TAPS_PER_CHANNEL); - } - else - { - mPolyphaseChannelizer.setSampleRate(sampleRate); - } + mPolyphaseChannelizer = new ComplexPolyphaseChannelizerM2(tunerSampleRate, + POLYPHASE_CHANNELIZER_TAPS_PER_CHANNEL); } catch(IllegalArgumentException iae) { - mLog.error("Could not create polyphase channelizer for sample rate [" + sampleRate + "]", iae); + mLog.error("Could not create polyphase channelizer for sample rate [" + tunerSampleRate + "]", iae); } catch(FilterDesignException fde) { - mLog.error("Could not create filter for polyphase channelizer for sample rate [" + sampleRate + "]", fde); + mLog.error("Could not create filter for polyphase channelizer for sample rate [" + tunerSampleRate + "]", fde); } - //Clear the previous channel synthesis filters so they can be recreated for the new channel sample rate + //Clear any previous channel synthesis filters so they can be recreated for the new channel sample rate mOutputProcessorFilters.clear(); - - //Broadcast the new channel sample rate - double updatedChannelSampleRate = mChannelCalculator.getChannelSampleRate(); - updateOutputProcessors(SourceEvent.sampleRateChange(updatedChannelSampleRate, "Channelizer Sample Rate Update")); } } @@ -600,9 +598,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor //Don't send the tuner's frequency change event down to the channels - it would cause chaos updateOutputProcessors(null); break; - case NOTIFICATION_SAMPLE_RATE_CHANGE: - updateChannelizerSampleRate(queuedSourceEvent.getValue().intValue()); - break; } queuedSourceEvent = mQueuedSourceEvents.poll(); diff --git a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java index bd6c4162..e0e7cc9b 100644 --- a/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java +++ b/src/main/java/io/github/dsheirer/gui/control/JFrequencyControl.java @@ -125,6 +125,8 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor mBlankCursor = Toolkit.getDefaultToolkit() .createCustomCursor(cursorImage, new Point(0, 0), "cursor"); + setTooltip(false); + revalidate(); } @@ -139,6 +141,26 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor } } + private void setTooltip(boolean locked) + { + String tooltip = null; + + if(locked) + { + tooltip = "Frequency control is locked. Disable decoding channels to unlock."; + } + else + { + tooltip = "Click on a frequency digit and type a frequency value, or click the upper/lower digits to " + + "change the value"; + } + + for(Digit digit: mDigits.values()) + { + digit.setToolTipText(tooltip); + } + } + /** * Receives a frequency change event invoked by another control. We don't * rebroadcast this event, just set the control to indicate the new frequency. @@ -165,6 +187,7 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor public void run() { setEnabled(false); + setTooltip(true); } }); break; @@ -175,6 +198,7 @@ public class JFrequencyControl extends JPanel implements ISourceEventProcessor public void run() { setEnabled(true); + setTooltip(false); } }); break; diff --git a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java index 66c30bf7..915290b1 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/Tuner.java +++ b/src/main/java/io/github/dsheirer/source/tuner/Tuner.java @@ -65,6 +65,10 @@ public abstract class Tuner implements ISourceEventProcessor case NOTIFICATION_SAMPLE_RATE_CHANGE: broadcast(new TunerEvent(Tuner.this, Event.SAMPLE_RATE)); break; + case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED: + case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED: + broadcast(new TunerEvent(Tuner.this, Event.LOCK_STATE_CHANGE)); + break; default: break; } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java b/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java index 3a0e0111..95fcee04 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerEvent.java @@ -2,33 +2,34 @@ package io.github.dsheirer.source.tuner; public class TunerEvent { - private Tuner mTuner; - private Event mEvent; - - public TunerEvent( Tuner tuner, Event event ) - { - mTuner = tuner; - mEvent = event; - } - - public Tuner getTuner() - { - return mTuner; - } + private Tuner mTuner; + private Event mEvent; - public Event getEvent() - { - return mEvent; - } - - public enum Event - { - ADD, - REMOVE, - CHANNEL_COUNT, - FREQUENCY, - SAMPLE_RATE, - REQUEST_MAIN_SPECTRAL_DISPLAY, - REQUEST_NEW_SPECTRAL_DISPLAY; - } + public TunerEvent(Tuner tuner, Event event) + { + mTuner = tuner; + mEvent = event; + } + + public Tuner getTuner() + { + return mTuner; + } + + public Event getEvent() + { + return mEvent; + } + + public enum Event + { + ADD, + LOCK_STATE_CHANGE, + REMOVE, + CHANNEL_COUNT, + FREQUENCY, + SAMPLE_RATE, + REQUEST_MAIN_SPECTRAL_DISPLAY, + REQUEST_NEW_SPECTRAL_DISPLAY; + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java index 615e20bb..77f028d0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerModel.java @@ -257,7 +257,16 @@ public class TunerModel extends AbstractTableModel implements Listener 0) + { + return channelCount + " (LOCKED)"; + } + else + { + return "0"; + } case SPECTRAL_DISPLAY_MAIN: return "Main"; case SPECTRAL_DISPLAY_NEW: diff --git a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java index f919377d..c6f79799 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java +++ b/src/main/java/io/github/dsheirer/source/tuner/TunerViewPanel.java @@ -1,17 +1,17 @@ /******************************************************************************* * SDR Trunk * Copyright (C) 2014-2016 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 ******************************************************************************/ @@ -19,18 +19,27 @@ package io.github.dsheirer.source.tuner; import com.jidesoft.swing.JideSplitPane; +import io.github.dsheirer.sample.Listener; import io.github.dsheirer.source.tuner.TunerEvent.Event; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import javax.swing.*; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.RowSorter; +import javax.swing.SortOrder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableRowSorter; -import java.awt.*; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; @@ -38,129 +47,150 @@ import java.util.List; public class TunerViewPanel extends JPanel { - private static final long serialVersionUID = 1L; - private final static Logger mLog = LoggerFactory.getLogger( TunerViewPanel.class ); + private static final long serialVersionUID = 1L; + private final static Logger mLog = LoggerFactory.getLogger(TunerViewPanel.class); - private TunerModel mTunerModel; - private JTable mTunerTable; - private TableRowSorter mRowSorter; - private JideSplitPane mSplitPane; - private TunerEditor mTunerEditor; - - public TunerViewPanel( TunerModel tunerModel ) - { - mTunerModel = tunerModel; - mTunerEditor = new TunerEditor( mTunerModel.getTunerConfigurationModel() ); - - init(); - } - - private void init() - { - setLayout( new MigLayout( "insets 0 0 0 0", "[fill,grow]", "[fill,grow]" ) ); + private TunerModel mTunerModel; + private JTable mTunerTable; + private TableRowSorter mRowSorter; + private JideSplitPane mSplitPane; + private TunerEditor mTunerEditor; - mRowSorter = new TableRowSorter<>( mTunerModel ); - List sortKeys = new ArrayList<>(); - sortKeys.add( new RowSorter.SortKey( TunerModel.TUNER_TYPE, SortOrder.ASCENDING ) ); - sortKeys.add( new RowSorter.SortKey( TunerModel.TUNER_ID, SortOrder.ASCENDING ) ); - mRowSorter.setSortKeys( sortKeys ); - - mTunerTable = new JTable( mTunerModel ); - mTunerTable.setRowSorter( mRowSorter ); - mTunerTable.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - mTunerTable.getSelectionModel().addListSelectionListener( new ListSelectionListener() - { - @Override - public void valueChanged( ListSelectionEvent event ) - { - if( !event.getValueIsAdjusting() ) - { - int row = mTunerTable.getSelectedRow(); - int modelRow = mTunerTable.convertRowIndexToModel( row ); + public TunerViewPanel(TunerModel tunerModel) + { + mTunerModel = tunerModel; + mTunerEditor = new TunerEditor(mTunerModel.getTunerConfigurationModel()); - mTunerEditor.setItem( mTunerModel.getTuner( modelRow ) ); - } - } - } ); - mTunerTable.addMouseListener( new MouseAdapter() - { - @Override - public void mouseClicked( MouseEvent e ) - { - int column = mTunerTable.columnAtPoint( e.getPoint() ); + init(); + } - if( column == TunerModel.SPECTRAL_DISPLAY_MAIN ) - { - int tableRow = mTunerTable.rowAtPoint( e.getPoint() ); - int modelRow = mTunerTable.convertRowIndexToModel( tableRow ); - - Tuner tuner = mTunerModel.getTuner( modelRow ); - - if( tuner != null ) - { - mTunerModel.broadcast( new TunerEvent( tuner, - Event.REQUEST_MAIN_SPECTRAL_DISPLAY ) ); - } - } - else if( column == TunerModel.SPECTRAL_DISPLAY_NEW ) - { - int tableRow = mTunerTable.rowAtPoint( e.getPoint() ); - int modelRow = mTunerTable.convertRowIndexToModel( tableRow ); - - Tuner tuner = mTunerModel.getTuner( modelRow ); - - if( tuner != null ) - { - mTunerModel.broadcast( new TunerEvent( tuner, - Event.REQUEST_NEW_SPECTRAL_DISPLAY ) ); - } - } - } - } ); - - TableCellRenderer renderer = new LinkCellRenderer(); - - mTunerTable.getColumnModel().getColumn( 5 ).setCellRenderer( renderer ); - mTunerTable.getColumnModel().getColumn( 6 ).setCellRenderer( renderer ); - - JScrollPane listScroller = new JScrollPane( mTunerTable ); - listScroller.setPreferredSize( new Dimension( 400, 20 ) ); + private void init() + { + setLayout(new MigLayout("insets 0 0 0 0", "[fill,grow]", "[fill,grow]")); - JScrollPane editorScroller = new JScrollPane( mTunerEditor ); - editorScroller.setPreferredSize( new Dimension( 400, 80 ) ); - - mSplitPane = new JideSplitPane(); - mSplitPane.setOrientation( JideSplitPane.VERTICAL_SPLIT ); - mSplitPane.add( listScroller ); - mSplitPane.add( editorScroller ); - - add( mSplitPane ); - } - - public class LinkCellRenderer extends DefaultTableCellRenderer - { - private static final long serialVersionUID = 1L; + mRowSorter = new TableRowSorter<>(mTunerModel); + List sortKeys = new ArrayList<>(); + sortKeys.add(new RowSorter.SortKey(TunerModel.TUNER_TYPE, SortOrder.ASCENDING)); + sortKeys.add(new RowSorter.SortKey(TunerModel.TUNER_ID, SortOrder.ASCENDING)); + mRowSorter.setSortKeys(sortKeys); - @Override - public Component getTableCellRendererComponent( JTable table, - Object value, boolean isSelected, boolean hasFocus, int row, - int column ) - { - JLabel label = (JLabel)super.getTableCellRendererComponent( table, - value, isSelected, hasFocus, row, column ); - - label.setForeground( Color.BLUE.brighter() ); - - if( column == TunerModel.SPECTRAL_DISPLAY_MAIN ) - { - label.setToolTipText( "Show this tuner in the main spectral display" ); - } - else if( column == TunerModel.SPECTRAL_DISPLAY_NEW ) - { - label.setToolTipText( "Show this tuner in a new spectral display" ); - } - - return label; - } - } + mTunerTable = new JTable(mTunerModel); + mTunerTable.setRowSorter(mRowSorter); + mTunerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + mTunerTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() + { + @Override + public void valueChanged(ListSelectionEvent event) + { + if(!event.getValueIsAdjusting()) + { + int row = mTunerTable.getSelectedRow(); + int modelRow = mTunerTable.convertRowIndexToModel(row); + + mTunerEditor.setItem(mTunerModel.getTuner(modelRow)); + } + } + }); + + mTunerModel.addListener(new Listener() + { + @Override + public void receive(TunerEvent tunerEvent) + { + switch(tunerEvent.getEvent()) + { + case LOCK_STATE_CHANGE: + int row = mTunerTable.getSelectedRow(); + + if(row >= 0) + { + int modelRow = mTunerTable.convertRowIndexToModel(row); + mTunerEditor.setItem(mTunerModel.getTuner(modelRow)); + } + break; + } + } + }); + + mTunerTable.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + int column = mTunerTable.columnAtPoint(e.getPoint()); + + if(column == TunerModel.SPECTRAL_DISPLAY_MAIN) + { + int tableRow = mTunerTable.rowAtPoint(e.getPoint()); + int modelRow = mTunerTable.convertRowIndexToModel(tableRow); + + Tuner tuner = mTunerModel.getTuner(modelRow); + + if(tuner != null) + { + mTunerModel.broadcast(new TunerEvent(tuner, + Event.REQUEST_MAIN_SPECTRAL_DISPLAY)); + } + } + else if(column == TunerModel.SPECTRAL_DISPLAY_NEW) + { + int tableRow = mTunerTable.rowAtPoint(e.getPoint()); + int modelRow = mTunerTable.convertRowIndexToModel(tableRow); + + Tuner tuner = mTunerModel.getTuner(modelRow); + + if(tuner != null) + { + mTunerModel.broadcast(new TunerEvent(tuner, + Event.REQUEST_NEW_SPECTRAL_DISPLAY)); + } + } + } + }); + + TableCellRenderer renderer = new LinkCellRenderer(); + + mTunerTable.getColumnModel().getColumn(5).setCellRenderer(renderer); + mTunerTable.getColumnModel().getColumn(6).setCellRenderer(renderer); + + JScrollPane listScroller = new JScrollPane(mTunerTable); + listScroller.setPreferredSize(new Dimension(400, 20)); + + JScrollPane editorScroller = new JScrollPane(mTunerEditor); + editorScroller.setPreferredSize(new Dimension(400, 80)); + + mSplitPane = new JideSplitPane(); + mSplitPane.setOrientation(JideSplitPane.VERTICAL_SPLIT); + mSplitPane.add(listScroller); + mSplitPane.add(editorScroller); + + add(mSplitPane); + } + + public class LinkCellRenderer extends DefaultTableCellRenderer + { + private static final long serialVersionUID = 1L; + + @Override + public Component getTableCellRendererComponent(JTable table, + Object value, boolean isSelected, boolean hasFocus, int row, + int column) + { + JLabel label = (JLabel)super.getTableCellRendererComponent(table, + value, isSelected, hasFocus, row, column); + + label.setForeground(Color.BLUE.brighter()); + + if(column == TunerModel.SPECTRAL_DISPLAY_MAIN) + { + label.setToolTipText("Show this tuner in the main spectral display"); + } + else if(column == TunerModel.SPECTRAL_DISPLAY_NEW) + { + label.setToolTipText("Show this tuner in a new spectral display"); + } + + return label; + } + } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java index a709bad9..f7a9d022 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerController.java @@ -80,8 +80,7 @@ public class AirspyTunerController extends USBTunerController public static final long FREQUENCY_MAX = 1800000000l; public static final long FREQUENCY_DEFAULT = 101100000; public static final double USABLE_BANDWIDTH_PERCENT = 0.90; - public static final AirspySampleRate DEFAULT_SAMPLE_RATE = - new AirspySampleRate(0, 10000000, "10.00 MHz"); + public static final AirspySampleRate DEFAULT_SAMPLE_RATE = new AirspySampleRate(0, 10000000, "10.00 MHz"); private static final long USB_TIMEOUT_MS = 2000l; //milliseconds private static final byte USB_INTERFACE = (byte) 0x0; @@ -168,7 +167,7 @@ public class AirspyTunerController extends USBTunerController try { - setSampleRate(DEFAULT_SAMPLE_RATE); + setSampleRate(mSampleRates.get(0)); } catch(IllegalArgumentException | LibUsbException | UsbException e) { @@ -292,8 +291,7 @@ public class AirspyTunerController extends USBTunerController } catch(UsbException e) { - throw new SourceException("Couldn't set sample rate [" + - rate.toString() + "]", e); + throw new SourceException("Couldn't set sample rate [" + rate.toString() + "]", e); } try @@ -314,8 +312,7 @@ public class AirspyTunerController extends USBTunerController } catch(Exception e) { - throw new SourceException("Couldn't apply gain settings from " - + "airspy config", e); + throw new SourceException("Couldn't apply gain settings from airspy config", e); } try @@ -389,8 +386,7 @@ public class AirspyTunerController extends USBTunerController * is not supported by the current firmware * @throws UsbException if there was a USB error */ - public void setSampleRate(AirspySampleRate rate) throws - LibUsbException, UsbException, SourceException + public void setSampleRate(AirspySampleRate rate) throws LibUsbException, UsbException, SourceException { if(rate.getRate() != mSampleRate) { @@ -398,8 +394,7 @@ public class AirspyTunerController extends USBTunerController if(result != 1) { - throw new UsbException("Error setting sample rate [" + - rate + "] rate - return value [" + result + "]"); + throw new UsbException("Error setting sample rate [" + rate + "] rate - return value [" + result + "]"); } else { @@ -607,8 +602,7 @@ public class AirspyTunerController extends USBTunerController * Queries the device for available sample rates. Will always provide at * least the default 10 MHz sample rate. */ - private void determineAvailableSampleRates() - throws LibUsbException, UsbException + private void determineAvailableSampleRates() throws LibUsbException, UsbException { mSampleRates.clear(); @@ -629,7 +623,6 @@ public class AirspyTunerController extends USBTunerController for(int x = 0; x < count; x++) { int rate = EndianUtils.readSwappedInteger(rawRates, (x * 4)); - mSampleRates.add(new AirspySampleRate(x, rate, formatSampleRate(rate))); } } diff --git a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java index e8ef26c1..51d068f9 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/airspy/AirspyTunerEditor.java @@ -43,7 +43,6 @@ import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; @@ -55,8 +54,7 @@ public class AirspyTunerEditor extends TunerConfigurationEditor { private static final long serialVersionUID = 1L; - private final static Logger mLog = - LoggerFactory.getLogger(AirspyTunerEditor.class); + private final static Logger mLog = LoggerFactory.getLogger(AirspyTunerEditor.class); private JTextField mConfigurationName; private JButton mTunerInfo; @@ -103,24 +101,6 @@ public class AirspyTunerEditor extends TunerConfigurationEditor return null; } - /** - * Updates the enabled state of the sample rate combo control. - * - * @param enabled state for the sample rate control - */ - @Override - public void setSampleRateControl(boolean enabled) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mSampleRateCombo.setEnabled(enabled); - } - }); - } - private void init() { setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", @@ -616,6 +596,21 @@ public class AirspyTunerEditor extends TunerConfigurationEditor return null; } + /** + * Updates the sample rate tooltip according to the tuner controller's lock state. + */ + private void updateSampleRateToolTip() + { + if(mController.isLocked()) + { + mSampleRateCombo.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + } + else + { + mSampleRateCombo.setToolTipText("Select a sample rate for the tuner"); + } + } + /** * Sets all controls to the argument enabled state */ @@ -631,7 +626,13 @@ public class AirspyTunerEditor extends TunerConfigurationEditor mTunerInfo.setEnabled(enabled); } - if(mSampleRateCombo.isEnabled() != enabled) + updateSampleRateToolTip(); + + if(mController.isLocked()) + { + mSampleRateCombo.setEnabled(false); + } + else if(mSampleRateCombo.isEnabled() != enabled) { mSampleRateCombo.setEnabled(enabled); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java index c328c3af..50891f32 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/configuration/TunerConfigurationEditor.java @@ -32,12 +32,4 @@ public abstract class TunerConfigurationEditor extends Editor mProcessors = new ArrayList<>(); + private List mProcessors = new CopyOnWriteArrayList<>(); public FrequencyController(Tunable tunable, long minFrequency, long maxFrequency, double frequencyCorrection) { @@ -95,9 +95,16 @@ public class FrequencyController */ public void setSampleRate(int sampleRate) throws SourceException { - mSampleRate = sampleRate; + if(!mLocked) + { + mSampleRate = sampleRate; - broadcastSampleRateChange(); + broadcastSampleRateChange(); + } + else + { + mLog.warn("Cannot change sample rate while tuner is is LOCKED state."); + } } /** @@ -108,6 +115,17 @@ public class FrequencyController return mSampleRate; } + /** + * Indicates if the specified frequency can be tuned (ie is within min/max frequency) by this controller. + * + * @param frequency to evaluate + * @return true if the frequency falls within the tuning range of this controller + */ + public boolean canTune(long frequency) + { + return getMinimumFrequency() <= frequency && frequency <= getMaximumFrequency(); + } + /** * Get frequency in hertz */ @@ -128,17 +146,6 @@ public class FrequencyController setFrequency(frequency, true); } - /** - * Indicates if the specified frequency can be tuned (ie is within min/max frequency) by this controller. - * - * @param frequency to evaluate - * @return true if the frequency falls within the tuning range of this controller - */ - public boolean canTune(long frequency) - { - return getMinimumFrequency() <= frequency && frequency <= getMaximumFrequency(); - } - /** * Set frequency with optional broadcast of frequency change event. This * method supports changing the frequency correction value without diff --git a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java index a2f78262..52f706d0 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/hackrf/HackRFTunerEditor.java @@ -41,7 +41,6 @@ import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; @@ -84,19 +83,6 @@ public class HackRFTunerEditor extends TunerConfigurationEditor return null; } - @Override - public void setSampleRateControl(boolean enabled) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mComboSampleRate.setEnabled(enabled); - } - }); - } - private void init() { setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", @@ -297,6 +283,21 @@ public class HackRFTunerEditor extends TunerConfigurationEditor add(mComboVGAGain); } + /** + * Updates the sample rate tooltip according to the tuner controller's lock state. + */ + private void updateSampleRateToolTip() + { + if(mController.isLocked()) + { + mComboSampleRate.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + } + else + { + mComboSampleRate.setToolTipText("Select a sample rate for the tuner"); + } + } + /** * Sets each of the tuner configuration controls to the enabled argument state */ @@ -317,7 +318,13 @@ public class HackRFTunerEditor extends TunerConfigurationEditor mFrequencyCorrection.setEnabled(enabled); } - if(mComboSampleRate.isEnabled() != enabled) + updateSampleRateToolTip(); + + if(mController.isLocked()) + { + mComboSampleRate.setEnabled(false); + } + else if(mComboSampleRate.isEnabled() != enabled) { mComboSampleRate.setEnabled(enabled); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java index 5bfa9382..b7b2f854 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/e4k/E4KTunerEditor.java @@ -45,7 +45,6 @@ import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; @@ -88,19 +87,6 @@ public class E4KTunerEditor extends TunerConfigurationEditor return null; } - @Override - public void setSampleRateControl(boolean enabled) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mComboSampleRate.setEnabled(enabled); - } - }); - } - private void init() { setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", @@ -354,6 +340,21 @@ public class E4KTunerEditor extends TunerConfigurationEditor add(mComboEnhanceGain); } + /** + * Updates the sample rate tooltip according to the tuner controller's lock state. + */ + private void updateSampleRateToolTip() + { + if(mController.isLocked()) + { + mComboSampleRate.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + } + else + { + mComboSampleRate.setToolTipText("Select a sample rate for the tuner"); + } + } + /** * Sets each of the tuner configuration controls to the enabled argument state */ @@ -374,7 +375,13 @@ public class E4KTunerEditor extends TunerConfigurationEditor mFrequencyCorrection.setEnabled(enabled); } - if(mComboSampleRate.isEnabled() != enabled) + updateSampleRateToolTip(); + + if(mController.isLocked()) + { + mComboSampleRate.setEnabled(false); + } + else if(mComboSampleRate.isEnabled() != enabled) { mComboSampleRate.setEnabled(enabled); } diff --git a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java index 1105ffba..acca7f4f 100644 --- a/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java +++ b/src/main/java/io/github/dsheirer/source/tuner/rtl/r820t/R820TTunerEditor.java @@ -45,7 +45,6 @@ import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; -import java.awt.EventQueue; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; @@ -89,19 +88,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor return null; } - @Override - public void setSampleRateControl(boolean enabled) - { - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - mComboSampleRate.setEnabled(enabled); - } - }); - } - private void init() { setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]", @@ -408,6 +394,21 @@ public class R820TTunerEditor extends TunerConfigurationEditor add(mComboVGAGain); } + /** + * Updates the sample rate tooltip according to the tuner controller's lock state. + */ + private void updateSampleRateToolTip() + { + if(mController.isLocked()) + { + mComboSampleRate.setToolTipText("Sample Rate is locked. Disable decoding channels to unlock."); + } + else + { + mComboSampleRate.setToolTipText("Select a sample rate for the tuner"); + } + } + /** * Sets each of the tuner configuration controls to the enabled argument state */ @@ -428,7 +429,13 @@ public class R820TTunerEditor extends TunerConfigurationEditor mFrequencyCorrection.setEnabled(enabled); } - if(mComboSampleRate.isEnabled() != enabled) + updateSampleRateToolTip(); + + if(mController.isLocked()) + { + mComboSampleRate.setEnabled(false); + } + else if(mComboSampleRate.isEnabled() != enabled) { mComboSampleRate.setEnabled(enabled); }