Merge pull request #363 from DSheirer/170-airspy-mini-sample-rates

#170 Add Support for Airspy Mini tuner's 3 and 6 MHz
This commit is contained in:
Denny Sheirer
2018-07-14 07:47:03 -04:00
committed by GitHub
18 changed files with 376 additions and 348 deletions

View File

@@ -68,9 +68,10 @@ public abstract class AbstractComplexPolyphaseChannelizer implements Listener<Re
* Sets the input sample rate for for this channelizer
* @param sampleRate in hertz
*/
public void setSampleRate(double sampleRate)
public void setRates(double sampleRate, int channelCount)
{
mSampleRate = sampleRate;
mChannelCount = channelCount;
mChannelSampleRate = mSampleRate / (double)mChannelCount;
}

View File

@@ -20,7 +20,6 @@ import io.github.dsheirer.source.tuner.channel.TunerChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
@@ -43,7 +42,7 @@ public class ChannelCalculator
private int mChannelCount;
private double mCenterFrequency;
private double mChannelBandwidth;
private double mOversampling = 1.0;
private double mOversampling = 2.0;
/**
* Calculates channel index(es) from a polyphase channelizer output to source a DDC polyphase channel source
@@ -137,9 +136,11 @@ public class ChannelCalculator
*
* @param sampleRate to use for channel count
*/
public void setSampleRate(double sampleRate)
public void setRates(double sampleRate, int channelCount)
{
setChannelCount((int)(sampleRate / getChannelBandwidth()));
mSampleRate = sampleRate;
mChannelCount = channelCount;
updateChannelBandwidth();
}
/**
@@ -544,7 +545,8 @@ public class ChannelCalculator
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("Channels:").append(getChannelCount());
sb.append("Channel Calculator Settings");
sb.append(" Channels:").append(getChannelCount());
sb.append(" Sample Rate:").append(getSampleRate());
sb.append(" Channel Bandwidth:").append(getChannelBandwidth());
sb.append(" Channel Rate:").append(getChannelSampleRate());
@@ -552,43 +554,4 @@ public class ChannelCalculator
sb.append(" Max:").append(getMaximumFrequency());
return sb.toString();
}
public static void main(String[] args)
{
mLog.debug("Starting ....");
double channelBandwidth = 12500.0;
double sampleRate = 100000.0; //Airspy
int channelCount = (int)(sampleRate / channelBandwidth);
double centerFrequency = 455000000;
ChannelCalculator calculator = new ChannelCalculator(sampleRate, channelCount, centerFrequency, 2.0);
mLog.debug(calculator.toString());
DecimalFormat decimalFormat = new DecimalFormat("000,000,000.0");
int wrapAroundIndex = calculator.getWrapAroundIndex();
IndexBoundaryPolicy indexBoundaryPolicy = IndexBoundaryPolicy.ADJUST_NEGATIVE;
for(int index = wrapAroundIndex; index < calculator.getChannelCount(); index++)
{
mLog.debug("Index:" + index + " Min:" + decimalFormat.format(calculator.getIndexMinimumFrequency(index, indexBoundaryPolicy)) +
" Center:" + decimalFormat.format(calculator.getIndexCenterFrequency(index, indexBoundaryPolicy)) +
" Max:" + decimalFormat.format(calculator.getIndexMaximumFrequency(index, indexBoundaryPolicy)));
}
indexBoundaryPolicy = IndexBoundaryPolicy.ADJUST_POSITIVE;
for(int index = 0; index <= wrapAroundIndex; index++)
{
mLog.debug("Index:" + index + " Min:" + decimalFormat.format(calculator.getIndexMinimumFrequency(index, indexBoundaryPolicy)) +
" Center:" + decimalFormat.format(calculator.getIndexCenterFrequency(index, indexBoundaryPolicy)) +
" Max:" + decimalFormat.format(calculator.getIndexMaximumFrequency(index, indexBoundaryPolicy)));
}
mLog.debug("Finished");
}
}

View File

@@ -145,7 +145,7 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
* @param sampleRate to channelize
* @return number of multiple of two channels that can be channelized.
*/
private static int getChannelCount(double sampleRate)
public static int getChannelCount(double sampleRate)
{
int channels = (int)(sampleRate / DEFAULT_MINIMUM_CHANNEL_BANDWIDTH);
@@ -164,12 +164,11 @@ public class ComplexPolyphaseChannelizerM2 extends AbstractComplexPolyphaseChann
* @param sampleRate in hertz
*/
@Override
public void setSampleRate(double sampleRate)
public void setRates(double sampleRate, int channelCount)
{
try
{
super.setSampleRate(sampleRate);
super.setRates(sampleRate, channelCount);
float[] filterTaps = FilterFactory.getSincM2Channelizer(getChannelSampleRate(), getChannelCount(),
mTapsPerChannel, false);

View File

@@ -108,8 +108,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
mBufferProcessor = new ContinuousBufferProcessor(200, 50);
mBufferProcessor.setListener(mBufferSourceEventMonitor);
updateChannelizerSampleRate(sampleRate);
}
/**
@@ -229,6 +227,7 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
default:
//TODO: create output processor for greater than 2 input channels
mLog.error("Request to create an output processor for unexpected channel index size:" + indexes.size());
mLog.info(mChannelCalculator.toString());
return null;
}
}
@@ -245,6 +244,8 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
{
//Note: the polyphase channel source has already been added to the mChannelSources in getChannel() method
checkChannelizerConfiguration();
mPolyphaseChannelizer.addChannel(channelSource);
mSourceEventBroadcaster.broadcast(SourceEvent.channelCountChange(getTunerChannelCount()));
@@ -312,10 +313,9 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
break;
case NOTIFICATION_SAMPLE_RATE_CHANGE:
//Update channel calculator immediately so that channels can be allocated
mChannelCalculator.setSampleRate(sourceEvent.getValue().doubleValue());
//Defer channelizer configuration changes to be handled on the buffer processor thread
mBufferSourceEventMonitor.receive(sourceEvent);
double sampleRate = sourceEvent.getValue().doubleValue();
int channelCount = (int)Math.ceil(sampleRate / MINIMUM_CHANNEL_BANDWIDTH);
mChannelCalculator.setRates(sampleRate, channelCount);
break;
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_LOCKED:
case NOTIFICATION_FREQUENCY_AND_SAMPLE_RATE_UNLOCKED:
@@ -325,7 +325,6 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
default:
mLog.info("Unrecognized source event: " + sourceEvent.toString());
break;
}
}
@@ -335,40 +334,39 @@ public class PolyphaseChannelManager implements ISourceEventProcessor
* Note: this method should only be invoked on the mBufferProcessor thread or prior to starting the mBufferProcessor.
* Sample rate source events will normally arrive via the incoming complex buffer stream from the mBufferProcessor
* and will be handled as they arrive.
*
* @param sampleRate to use for the channelizer
*/
private void updateChannelizerSampleRate(double sampleRate)
private void checkChannelizerConfiguration()
{
if(mPolyphaseChannelizer == null || Math.abs(mPolyphaseChannelizer.getSampleRate() - sampleRate) > 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();

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -257,7 +257,16 @@ public class TunerModel extends AbstractTableModel implements Listener<TunerEven
return 0;
}
case CHANNEL_COUNT:
return tuner.getChannelSourceManager().getTunerChannelCount();
int channelCount = tuner.getChannelSourceManager().getTunerChannelCount();
if(channelCount > 0)
{
return channelCount + " (LOCKED)";
}
else
{
return "0";
}
case SPECTRAL_DISPLAY_MAIN:
return "Main";
case SPECTRAL_DISPLAY_NEW:

View File

@@ -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 <http://www.gnu.org/licenses/>
******************************************************************************/
@@ -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<TunerModel> 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<TunerModel> mRowSorter;
private JideSplitPane mSplitPane;
private TunerEditor mTunerEditor;
mRowSorter = new TableRowSorter<>( mTunerModel );
List<RowSorter.SortKey> 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<RowSorter.SortKey> 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<TunerEvent>()
{
@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;
}
}
}

View File

@@ -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)));
}
}

View File

@@ -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);
}

View File

@@ -32,12 +32,4 @@ public abstract class TunerConfigurationEditor extends Editor<TunerConfiguration
{
return mTunerConfigurationModel;
}
/**
* Sets the state of the sample rate editor control. When enabled, users are allowed to change the sample rate
* of the tuner. When disabled, user controls should NOT allow sample rate changes.
*
* @param enabled state for the sample rate control
*/
public abstract void setSampleRateControl(boolean enabled);
}

View File

@@ -86,12 +86,6 @@ public class FCD1TunerEditor extends TunerConfigurationEditor
return null;
}
@Override
public void setSampleRateControl(boolean enabled)
{
//no-op
}
private void init()
{
setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]",

View File

@@ -76,12 +76,6 @@ public class FCD2TunerEditor extends TunerConfigurationEditor
return null;
}
@Override
public void setSampleRateControl(boolean enabled)
{
//no-op
}
private void init()
{
setLayout(new MigLayout("fill,wrap 4", "[right][grow,fill][right][grow,fill]",

View File

@@ -22,8 +22,8 @@ import io.github.dsheirer.source.SourceException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
public class FrequencyController
{
@@ -38,7 +38,7 @@ public class FrequencyController
private double mSampleRate = 0.0d;
private boolean mLocked = false;
private List<ISourceEventProcessor> mProcessors = new ArrayList<>();
private List<ISourceEventProcessor> 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

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}