/******************************************************************************* * SDR Trunk * Copyright (C) 2015-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/> ******************************************************************************/ package source.tuner.airspy; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.text.DecimalFormat; import java.util.List; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JSeparator; import javax.swing.JSlider; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerModel; import javax.swing.SpinnerNumberModel; import javax.swing.SwingConstants; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.usb.UsbException; import net.miginfocom.swing.MigLayout; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.usb4java.LibUsbException; import source.SourceException; import source.tuner.airspy.AirspyTunerController.Gain; import source.tuner.airspy.AirspyTunerController.GainMode; import source.tuner.configuration.TunerConfiguration; import source.tuner.configuration.TunerConfigurationEditor; import source.tuner.configuration.TunerConfigurationEvent; import source.tuner.configuration.TunerConfigurationModel; import source.tuner.configuration.TunerConfigurationEvent.Event; public class AirspyTunerEditor extends TunerConfigurationEditor { private static final long serialVersionUID = 1L; private final static Logger mLog = LoggerFactory.getLogger( AirspyTunerEditor.class ); private JTextField mConfigurationName; private JButton mTunerInfo; private JComboBox<AirspySampleRate> mSampleRateCombo; private JSpinner mFrequencyCorrection; private JComboBox<GainMode> mGainModeCombo; private JSlider mMasterGain; private JLabel mMasterGainValueLabel; private JSlider mIFGain; private JLabel mIFGainValueLabel; private JSlider mLNAGain; private JLabel mLNAGainValueLabel; private JSlider mMixerGain; private JLabel mMixerGainValueLabel; private JCheckBox mLNAAGC; private JCheckBox mMixerAGC; private AirspyTunerController mController; private boolean mLoading; public AirspyTunerEditor( TunerConfigurationModel tunerConfigurationModel, AirspyTuner tuner ) { super( tunerConfigurationModel ); mController = tuner.getController(); init(); } private AirspyTunerConfiguration getConfiguration() { if( hasItem() ) { return (AirspyTunerConfiguration)getItem(); } return null; } private void init() { setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]", "[][][][][][grow]" ) ); add( new JLabel( "Airspy Tuner Configuration" ), "span,align center" ); mConfigurationName = new JTextField(); mConfigurationName.setEnabled( false ); mConfigurationName.addFocusListener( new FocusListener() { @Override public void focusLost( FocusEvent e ) { save(); } @Override public void focusGained( FocusEvent e ) {} } ); add( new JLabel( "Name:" ) ); add( mConfigurationName, "span 2" ); mTunerInfo = new JButton( "Tuner Info" ); mTunerInfo.setEnabled( false ); mTunerInfo.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { JOptionPane.showMessageDialog( AirspyTunerEditor.this, getTunerInfo(), "Tuner Info", JOptionPane.INFORMATION_MESSAGE ); } } ); add( mTunerInfo ); /** * Sample Rate */ add( new JLabel( "Sample Rate:" ) ); List<AirspySampleRate> rates = mController.getSampleRates(); mSampleRateCombo = new JComboBox<AirspySampleRate>( new DefaultComboBoxModel<AirspySampleRate>( rates.toArray( new AirspySampleRate[ rates.size() ] ) ) ); mSampleRateCombo.setEnabled( false ); mSampleRateCombo.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem(); try { mController.setSampleRate( rate ); save(); } catch ( LibUsbException | UsbException | SourceException e1 ) { JOptionPane.showMessageDialog( AirspyTunerEditor.this, "Couldn't set sample rate to " + rate.getLabel() ); mLog.error( "Error setting airspy sample rate", e1 ); } } } ); add( mSampleRateCombo ); /** * Frequency Correction */ SpinnerModel model = new SpinnerNumberModel( 0.0, //initial value -1000.0, //min 1000.0, //max 0.1 ); //step mFrequencyCorrection = new JSpinner( model ); mFrequencyCorrection.setEnabled( false ); JSpinner.NumberEditor editor = (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); DecimalFormat format = editor.getFormat(); format.setMinimumFractionDigits( 1 ); editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); mFrequencyCorrection.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent e ) { final double value = ((SpinnerNumberModel)mFrequencyCorrection .getModel()).getNumber().doubleValue(); try { mController.setFrequencyCorrection( value ); } catch ( SourceException e1 ) { mLog.error( "Error setting frequency correction value", e1 ); } save(); } } ); add( new JLabel( "PPM:" ) ); add( mFrequencyCorrection ); add( new JSeparator(), "span,growx,push" ); /** * Gain Mode */ add( new JLabel( "Gain Mode:" ) ); mGainModeCombo = new JComboBox<AirspyTunerController.GainMode>( GainMode.values() ); mGainModeCombo.setEnabled( false ); mGainModeCombo.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); updateGainComponents( mode ); if( hasItem() ) { AirspyTunerConfiguration airspy = getConfiguration(); Gain gain = airspy.getGain(); if( mode == GainMode.CUSTOM ) { mIFGain.setValue( airspy.getIFGain() ); mMixerGain.setValue( airspy.getMixerGain() ); mMixerAGC.setSelected( airspy.isMixerAGC() ); mLNAGain.setValue( airspy.getLNAGain() ); mLNAAGC.setSelected( airspy.isLNAAGC() ); } else { mMixerAGC.setSelected( false ); mLNAAGC.setSelected( false ); mMasterGain.setValue( gain.getValue() ); mIFGain.setValue( gain.getIF() ); mMixerGain.setValue( gain.getMixer() ); mLNAGain.setValue( gain.getLNA() ); } } save(); } } ); add( mGainModeCombo, "wrap" ); /** * Gain */ add( new JLabel( "Master:" ) ); mMasterGain = new JSlider( JSlider.HORIZONTAL, AirspyTunerController.GAIN_MIN, AirspyTunerController.GAIN_MAX, AirspyTunerController.GAIN_MIN ); mMasterGain.setMajorTickSpacing( 1 ); mMasterGain.setPaintTicks( true ); mMasterGain.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent event ) { GainMode mode = (GainMode)mGainModeCombo.getSelectedItem(); int value = mMasterGain.getValue(); Gain gain = Gain.getGain( mode, value ); try { mController.setGain( gain ); save(); mMasterGainValueLabel.setText( String.valueOf( gain.getValue() ) ); } catch ( Exception e ) { mLog.error( "Couldn't set airspy gain to:" + gain.name(), e ); JOptionPane.showMessageDialog( mMasterGain, "Couldn't set gain value to " + gain.getValue() ); } } } ); add( mMasterGain, "span 2" ); mMasterGainValueLabel = new JLabel( "0" ); add( mMasterGainValueLabel ); add( new JLabel( "IF:" ) ); mIFGain = new JSlider( JSlider.HORIZONTAL, AirspyTunerController.IF_GAIN_MIN, AirspyTunerController.IF_GAIN_MAX, AirspyTunerController.IF_GAIN_MIN ); mIFGain.setMajorTickSpacing( 1 ); mIFGain.setPaintTicks( true ); mIFGain.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent event ) { int gain = mIFGain.getValue(); try { mController.setIFGain( gain ); save(); mIFGainValueLabel.setText( String.valueOf( gain ) ); } catch ( Exception e ) { mLog.error( "Couldn't set airspy IF gain to:" + gain, e ); JOptionPane.showMessageDialog( mIFGain, "Couldn't set IF gain value to " + gain ); } } } ); add( mIFGain, "span 2" ); mIFGainValueLabel = new JLabel( "0" ); add( mIFGainValueLabel ); /** * Mixer Gain */ add( new JLabel( "Mixer:" ) ); mMixerGain = new JSlider( JSlider.HORIZONTAL, AirspyTunerController.MIXER_GAIN_MIN, AirspyTunerController.MIXER_GAIN_MAX, AirspyTunerController.MIXER_GAIN_MIN ); mMixerGain.setMajorTickSpacing( 1 ); mMixerGain.setPaintTicks( true ); mMixerGain.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent event ) { int gain = mMixerGain.getValue(); try { mController.setMixerGain( gain ); save(); mMixerGainValueLabel.setText( String.valueOf( gain ) ); } catch ( Exception e ) { mLog.error( "Couldn't set airspy Mixer gain to:" + gain, e ); JOptionPane.showMessageDialog( mIFGain, "Couldn't set Mixer gain value to " + gain ); } } } ); add( mMixerGain, "span 2" ); mMixerGainValueLabel = new JLabel( "0" ); add( mMixerGainValueLabel ); add( new JLabel( "LNA:" ) ); mLNAGain = new JSlider( JSlider.HORIZONTAL, AirspyTunerController.LNA_GAIN_MIN, AirspyTunerController.LNA_GAIN_MAX, AirspyTunerController.LNA_GAIN_MIN ); mLNAGain.setMajorTickSpacing( 1 ); mLNAGain.setPaintTicks( true ); mLNAGain.addChangeListener( new ChangeListener() { @Override public void stateChanged( ChangeEvent event ) { int gain = mLNAGain.getValue(); try { mController.setLNAGain( gain ); save(); mLNAGainValueLabel.setText( String.valueOf( gain ) ); } catch ( Exception e ) { mLog.error( "Couldn't set airspy LNA gain to:" + gain, e ); JOptionPane.showMessageDialog( mIFGain, "Couldn't set LNA gain value to " + gain ); } } } ); add( mLNAGain, "span 2" ); mLNAGainValueLabel = new JLabel( "0" ); add( mLNAGainValueLabel ); mMixerAGC = new JCheckBox( "Mixer AGC" ); mMixerAGC.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { try { mController.setMixerAGC( mMixerAGC.isSelected() ); mMixerGain.setEnabled( !mMixerAGC.isSelected() ); save(); } catch ( Exception e1 ) { mLog.error( "Error setting Mixer AGC Enabled" ); } } } ); add( mMixerAGC, "span 2,center" ); mLNAAGC = new JCheckBox( "LNA AGC" ); mLNAAGC.addActionListener( new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { try { mController.setLNAAGC( mLNAAGC.isSelected() ); mLNAGain.setEnabled( !mLNAAGC.isSelected() ); save(); } catch ( Exception e1 ) { mLog.error( "Error setting LNA AGC Enabled" ); } } } ); add( mLNAAGC, "span 2,center" ); } /** * Updates the enabled state of each of the gain controls according to the * specified gain mode. The master gain control is enabled for linearity * and sensitivity and the individual gain controls are disabled, and * vice-versa for custom mode. */ private void updateGainComponents( GainMode mode ) { switch( mode ) { case LINEARITY: if( !mMasterGain.isEnabled() ) { mMasterGain.setEnabled( true ); mMasterGainValueLabel.setEnabled( true ); } if( mIFGain.isEnabled() ) { mIFGain.setEnabled( false ); mIFGainValueLabel.setEnabled( false ); } if( mLNAGain.isEnabled() ) { mLNAAGC.setEnabled( false ); mLNAGainValueLabel.setEnabled( false ); mLNAGain.setEnabled( false ); } if( mMixerGain.isEnabled() ) { mMixerGainValueLabel.setEnabled( false ); mMixerAGC.setEnabled( false ); mMixerGain.setEnabled( false ); } break; case SENSITIVITY: if( !mMasterGain.isEnabled() ) { mMasterGain.setEnabled( true ); mMasterGainValueLabel.setEnabled( true ); } if( mIFGain.isEnabled() ) { mIFGain.setEnabled( false ); mIFGainValueLabel.setEnabled( false ); } if( mLNAGain.isEnabled() ) { mLNAAGC.setEnabled( false ); mLNAGainValueLabel.setEnabled( false ); mLNAGain.setEnabled( false ); } if( mMixerGain.isEnabled() ) { mMixerGainValueLabel.setEnabled( false ); mMixerAGC.setEnabled( false ); mMixerGain.setEnabled( false ); } break; case CUSTOM: if( mMasterGain.isEnabled() ) { mMasterGain.setEnabled( false ); mMasterGainValueLabel.setEnabled( false ); } if( !mIFGain.isEnabled() ) { mIFGain.setEnabled( true ); mIFGainValueLabel.setEnabled( true ); } if( !mLNAGain.isEnabled() ) { mLNAAGC.setEnabled( true ); mLNAGainValueLabel.setEnabled( true ); mLNAGain.setEnabled( true ); } if( !mMixerGain.isEnabled() ) { mMixerGainValueLabel.setEnabled( true ); mMixerAGC.setEnabled( true ); mMixerGain.setEnabled( true ); } default: break; } } @Override public void save() { if( hasItem() && !mLoading ) { AirspyTunerConfiguration config = getConfiguration(); config.setName( mConfigurationName.getText() ); config.setSampleRate( ((AirspySampleRate)mSampleRateCombo.getSelectedItem()).getRate() ); double value = ((SpinnerNumberModel)mFrequencyCorrection.getModel()).getNumber().doubleValue(); config.setFrequencyCorrection( value ); Gain gain = Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ); config.setGain( gain ); config.setIFGain( mIFGain.getValue() ); config.setMixerGain( mMixerGain.getValue() ); config.setLNAGain( mLNAGain.getValue() ); config.setMixerAGC( mMixerAGC.isSelected() ); config.setLNAAGC( mLNAAGC.isSelected() ); getTunerConfigurationModel().broadcast( new TunerConfigurationEvent( config, Event.CHANGE ) ); } } private AirspySampleRate fromValue( int value ) { List<AirspySampleRate> rates = mController.getSampleRates(); for( AirspySampleRate rate: rates ) { if( rate.getRate() == value ) { return rate; } } if( rates.size() > 0 ) { return rates.get( 0 ); } return null; } /** * Sets all controls to the argument enabled state */ private void setControlsEnabled( boolean enabled ) { if( mConfigurationName.isEnabled() != enabled ) { mConfigurationName.setEnabled( enabled ); } if( mTunerInfo.isEnabled() != enabled ) { mTunerInfo.setEnabled( enabled ); } if( mSampleRateCombo.isEnabled() != enabled ) { mSampleRateCombo.setEnabled( enabled ); } if( mFrequencyCorrection.isEnabled() != enabled ) { mFrequencyCorrection.setEnabled( enabled ); } if( mGainModeCombo.isEnabled() != enabled ) { mGainModeCombo.setEnabled( enabled ); } if( mMasterGain.isEnabled() != enabled ) { mMasterGain.setEnabled( enabled ); } if( mIFGain.isEnabled() != enabled ) { mIFGain.setEnabled( enabled ); } if( mMixerGain.isEnabled() != enabled ) { mMixerGain.setEnabled( enabled ); } if( mMixerAGC.isEnabled() != enabled ) { mMixerAGC.setEnabled( enabled ); } if( mLNAGain.isEnabled() != enabled ) { mLNAGain.setEnabled( enabled ); } if( mLNAAGC.isEnabled() != enabled ) { mLNAAGC.setEnabled( enabled ); } } /** * Applies the tuner configuration to the editor. If the configuration is assigned to the * currently selected tuner, then the settings are applied. */ @Override public void setItem( TunerConfiguration config ) { super.setItem( config ); mLoading = true; if( hasItem() && getItem().isAssigned() ) { AirspyTunerConfiguration airspy = getConfiguration(); setControlsEnabled( true ); mConfigurationName.setText( airspy.getName() ); AirspySampleRate rate = fromValue( airspy.getSampleRate() ); if( rate != null ) { mSampleRateCombo.setSelectedItem( rate ); } mFrequencyCorrection.setValue( airspy.getFrequencyCorrection() ); GainMode mode = Gain.getGainMode( airspy.getGain() ); //This will set the master, IF, Mixer, LNA and AGC controls mGainModeCombo.setSelectedItem( mode ); if( mode == GainMode.CUSTOM ) { mMasterGain.setValue( airspy.getGain().getValue() ); mIFGain.setValue( airspy.getIFGain() ); if( airspy.isMixerAGC() ) { mMixerAGC.setSelected( airspy.isMixerAGC() ); mMixerGain.setEnabled( false ); } else { mMixerGain.setValue( airspy.getMixerGain() ); } if( airspy.isLNAAGC() ) { mLNAAGC.setSelected( airspy.isLNAAGC() ); mLNAGain.setEnabled( false ); } else { mLNAGain.setValue( airspy.getLNAGain() ); } } else { mMixerAGC.setSelected( false ); mLNAAGC.setSelected( false ); Gain gain = airspy.getGain(); mMasterGain.setValue( gain.getValue() ); mIFGain.setValue( gain.getIF() ); mMixerGain.setValue( gain.getMixer() ); mLNAGain.setValue( gain.getLNA() ); } } else { setControlsEnabled( false ); mConfigurationName.setText( hasItem() ? getItem().getName() : "" ); } mLoading = false; } private String getTunerInfo() { StringBuilder sb = new StringBuilder(); AirspyDeviceInformation info = mController.getDeviceInfo(); sb.append( "<html><h3>Airspy Tuner</h3>" ); sb.append( "<b>Serial: </b>" ); sb.append( info.getSerialNumber() ); sb.append( "<br>" ); sb.append( "<b>Firmware: </b>" ); String[] firmware = info.getVersion().split( " " ); sb.append( firmware.length > 1 ? firmware[ 0 ] : info.getVersion() ); sb.append( "<br>" ); sb.append( "<b>Part: </b>" ); sb.append( info.getPartNumber() ); sb.append( "<br>" ); sb.append( "<b>Board ID: </b>" ); sb.append( info.getBoardID().getLabel() ); sb.append( "<br>" ); return sb.toString(); } }