mirror of
https://github.com/DSheirer/sdrtrunk.git
synced 2026-11-12 08:37:25 -05:00
wip: #34 - finishes the airspy tuner editor panel and adds tuner info
button to the r820t tuner editor to view serial number and misc info.
This commit is contained in:
@@ -100,6 +100,7 @@ public class TunerEditor extends Editor<Tuner>
|
||||
listPanel.add( tableScroller, "span" );
|
||||
|
||||
mNewConfigurationButton.setEnabled( false );
|
||||
mNewConfigurationButton.setToolTipText( "Create a new configuration for the currently selected tuner" );
|
||||
mNewConfigurationButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
@@ -120,6 +121,7 @@ public class TunerEditor extends Editor<Tuner>
|
||||
listPanel.add( mNewConfigurationButton );
|
||||
|
||||
mDeleteConfigurationButton.setEnabled( false );
|
||||
mDeleteConfigurationButton.setToolTipText( "Deletes the currently selected tuner" );
|
||||
mDeleteConfigurationButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
@@ -140,6 +142,7 @@ public class TunerEditor extends Editor<Tuner>
|
||||
listPanel.add( mDeleteConfigurationButton );
|
||||
|
||||
mAssignConfigurationButton.setEnabled( false );
|
||||
mAssignConfigurationButton.setToolTipText( "Assigns to the currently selected tuner configuration to the selected tuner" );
|
||||
mAssignConfigurationButton.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
|
||||
@@ -1,77 +0,0 @@
|
||||
package source.tuner.airspy;
|
||||
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2015 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
public class AirspyInformationPanel extends JPanel
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private AirspyTunerController mController;
|
||||
|
||||
/**
|
||||
* Displays Airspy firmware, serial number and part identifier
|
||||
*/
|
||||
public AirspyInformationPanel( AirspyTunerController controller )
|
||||
{
|
||||
mController = controller;
|
||||
|
||||
initGUI();
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2",
|
||||
"[right,grow][grow]",
|
||||
"[][][][][grow]" ) );
|
||||
|
||||
AirspyDeviceInformation info = mController.getDeviceInfo();
|
||||
|
||||
add( new JLabel( "Serial:" ) );
|
||||
add( new JLabel( info.getSerialNumber() ) );
|
||||
add( new JLabel( "Firmware:" ) );
|
||||
|
||||
//Split the firmware on spaces and use first value -- expose full
|
||||
//string in a tool tip
|
||||
String[] firmware = info.getVersion().split( " " );
|
||||
|
||||
JLabel firmwareLabel;
|
||||
|
||||
if( firmware.length > 1 )
|
||||
{
|
||||
firmwareLabel = new JLabel( firmware[ 0 ] );
|
||||
}
|
||||
else
|
||||
{
|
||||
firmwareLabel = new JLabel( info.getVersion() );
|
||||
}
|
||||
|
||||
firmwareLabel.setToolTipText( info.getVersion() );
|
||||
|
||||
add( firmwareLabel );
|
||||
add( new JLabel( "Part:" ) );
|
||||
add( new JLabel( info.getPartNumber() ) );
|
||||
add( new JLabel( "Board ID:" ) );
|
||||
add( new JLabel( info.getBoardID().getLabel() ) );
|
||||
}
|
||||
}
|
||||
@@ -1,695 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2014 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.util.List;
|
||||
|
||||
import javax.swing.ComboBoxModel;
|
||||
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.JPanel;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JSlider;
|
||||
import javax.swing.JTextField;
|
||||
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 settings.SettingsManager;
|
||||
import source.SourceException;
|
||||
import source.tuner.airspy.AirspyTunerController.Gain;
|
||||
import source.tuner.airspy.AirspyTunerController.GainMode;
|
||||
|
||||
public class AirspyTunerConfigurationPanel extends JPanel
|
||||
{
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( AirspyTunerConfigurationPanel.class );
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private SettingsManager mSettingsManager;
|
||||
private AirspyTunerController mController;
|
||||
private AirspyTunerConfiguration mSelectedConfig;
|
||||
|
||||
private JButton mNewConfiguration;
|
||||
private JButton mDeleteConfiguration;
|
||||
private JComboBox<AirspyTunerConfiguration> mComboConfigurations;
|
||||
|
||||
private JTextField mName;
|
||||
|
||||
private JComboBox<AirspySampleRate> mSampleRateCombo;
|
||||
|
||||
private JComboBox<GainMode> mGainModeCombo;
|
||||
private JLabel mGainLabel;
|
||||
private JLabel mGainFillerLabel;
|
||||
private JLabel mGainValueLabel;
|
||||
private JSlider mGain;
|
||||
|
||||
private JLabel mIFGainLabel;
|
||||
private JLabel mIFGainFillerLabel;
|
||||
private JLabel mIFGainValueLabel;
|
||||
private JSlider mIFGain;
|
||||
|
||||
private JLabel mLNAGainLabel;
|
||||
private JCheckBox mLNAAGC;
|
||||
private JLabel mLNAGainValueLabel;
|
||||
private JSlider mLNAGain;
|
||||
|
||||
private JLabel mMixerGainValueLabel;
|
||||
private JCheckBox mMixerAGC;
|
||||
private JLabel mMixerGainLabel;
|
||||
private JSlider mMixerGain;
|
||||
|
||||
|
||||
public AirspyTunerConfigurationPanel( SettingsManager settingsManager,
|
||||
AirspyTunerController controller )
|
||||
{
|
||||
mSettingsManager = settingsManager;
|
||||
mController = controller;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the gui components
|
||||
*/
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 4", "[left][grow][grow][right]",
|
||||
"[][][][][][][grow]" ) );
|
||||
|
||||
/**
|
||||
* Tuner configuration selector
|
||||
*/
|
||||
mComboConfigurations = new JComboBox<AirspyTunerConfiguration>();
|
||||
|
||||
mComboConfigurations.setModel( getModel() );
|
||||
|
||||
/* Determine which tuner configuration should be selected/displayed */
|
||||
// TunerConfigurationAssignment savedConfig = null;
|
||||
String serial = null;
|
||||
|
||||
serial = mController.getDeviceInfo().getSerialNumber();
|
||||
|
||||
if( serial != null )
|
||||
{
|
||||
// savedConfig = mSettingsManager.getSelectedTunerConfiguration(
|
||||
// TunerType.AIRSPY_R820T, serial );
|
||||
}
|
||||
|
||||
// if( savedConfig != null )
|
||||
// {
|
||||
// mSelectedConfig = getNamedConfiguration(
|
||||
// savedConfig.getTunerConfigurationName() );
|
||||
// }
|
||||
|
||||
/* If we couldn't determine the saved/selected config, use the first one */
|
||||
if( mSelectedConfig == null )
|
||||
{
|
||||
mSelectedConfig = mComboConfigurations.getItemAt( 0 );
|
||||
|
||||
//Store this config as the default for this tuner at this address
|
||||
|
||||
if( serial != null )
|
||||
{
|
||||
// mSettingsManager.setSelectedTunerConfiguration( TunerType.AIRSPY_R820T,
|
||||
// serial, mSelectedConfig );
|
||||
}
|
||||
}
|
||||
|
||||
mComboConfigurations.setSelectedItem( mSelectedConfig );
|
||||
|
||||
mComboConfigurations.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
AirspyTunerConfiguration selected =
|
||||
(AirspyTunerConfiguration)mComboConfigurations
|
||||
.getItemAt( mComboConfigurations.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
update( selected );
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add( new JLabel( "Config:" ) );
|
||||
add( mComboConfigurations, "span,growx,push" );
|
||||
|
||||
add( new JSeparator(), "span,growx,push" );
|
||||
|
||||
|
||||
/**
|
||||
* Tuner Configuration Name
|
||||
*/
|
||||
mName = new JTextField();
|
||||
mName.setText( mSelectedConfig.getName() );
|
||||
|
||||
mName.addFocusListener( new FocusListener()
|
||||
{
|
||||
@Override
|
||||
public void focusLost( FocusEvent e )
|
||||
{
|
||||
mSelectedConfig.setName( mName.getText() );
|
||||
save();
|
||||
}
|
||||
@Override
|
||||
public void focusGained( FocusEvent e ) {}
|
||||
} );
|
||||
|
||||
add( new JLabel( "Name:" ) );
|
||||
add( mName, "span,growx,push" );
|
||||
|
||||
/**
|
||||
* 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.setSelectedItem( mController.getSampleRate() );
|
||||
|
||||
mSampleRateCombo.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
AirspySampleRate rate = (AirspySampleRate)mSampleRateCombo.getSelectedItem();
|
||||
|
||||
try
|
||||
{
|
||||
mController.setSampleRate( rate );
|
||||
mSelectedConfig.setSampleRate( rate.getRate() );
|
||||
save();
|
||||
}
|
||||
catch ( LibUsbException | UsbException e1 )
|
||||
{
|
||||
JOptionPane.showMessageDialog( AirspyTunerConfigurationPanel.this,
|
||||
"Couldn't set sample rate to " + rate.getLabel() );
|
||||
|
||||
mLog.error( "Error setting airspy sample rate", e1 );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mSampleRateCombo, "span 3,growx" );
|
||||
|
||||
add( new JSeparator(), "span,growx,push" );
|
||||
|
||||
/**
|
||||
* Gain Mode
|
||||
*/
|
||||
add( new JLabel( "Gain Mode:" ) );
|
||||
mGainModeCombo = new JComboBox<AirspyTunerController.GainMode>( GainMode.values() );
|
||||
mGainModeCombo.setSelectedItem( Gain.getGainMode( mSelectedConfig.getGain() ) );
|
||||
mGainModeCombo.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
GainMode mode = (GainMode)mGainModeCombo.getSelectedItem();
|
||||
updateGainComponents( mode );
|
||||
|
||||
switch( mode )
|
||||
{
|
||||
case LINEARITY:
|
||||
mGain.setValue( AirspyTunerController.LINEARITY_GAIN_DEFAULT.getValue() );
|
||||
mSelectedConfig.setGain( AirspyTunerController.LINEARITY_GAIN_DEFAULT );
|
||||
break;
|
||||
case SENSITIVITY:
|
||||
mGain.setValue( AirspyTunerController.SENSITIVITY_GAIN_DEFAULT.getValue() );
|
||||
mSelectedConfig.setGain( AirspyTunerController.SENSITIVITY_GAIN_DEFAULT );
|
||||
break;
|
||||
case CUSTOM:
|
||||
default:
|
||||
mLNAGain.setValue( mSelectedConfig.getLNAGain() );
|
||||
mMixerGain.setValue( mSelectedConfig.getMixerGain() );
|
||||
mIFGain.setValue( mSelectedConfig.getIFGain() );
|
||||
mSelectedConfig.setGain( Gain.CUSTOM );
|
||||
break;
|
||||
}
|
||||
|
||||
save();
|
||||
}
|
||||
} );
|
||||
|
||||
add( mGainModeCombo, "wrap" );
|
||||
|
||||
/**
|
||||
* Gain
|
||||
*/
|
||||
mGainLabel = new JLabel( "Gain" );
|
||||
add( mGainLabel );
|
||||
mGainFillerLabel = new JLabel( "" );
|
||||
add( mGainFillerLabel, "span 2" );
|
||||
mGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getGain().getValue() ) );
|
||||
add( mGainValueLabel, "wrap" );
|
||||
mGain = new JSlider( JSlider.HORIZONTAL,
|
||||
AirspyTunerController.GAIN_MIN,
|
||||
AirspyTunerController.GAIN_MAX,
|
||||
mSelectedConfig.getGain().getValue() );
|
||||
mGain.setMajorTickSpacing( 1 );
|
||||
mGain.setPaintTicks( true );
|
||||
|
||||
mGain.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
GainMode mode = (GainMode)mGainModeCombo.getSelectedItem();
|
||||
int value = mGain.getValue();
|
||||
Gain gain = Gain.getGain( mode, value );
|
||||
|
||||
try
|
||||
{
|
||||
mController.setGain( gain );
|
||||
mSelectedConfig.setGain( gain );
|
||||
save();
|
||||
mGainValueLabel.setText( String.valueOf( gain.getValue() ) );
|
||||
}
|
||||
catch ( Exception e )
|
||||
{
|
||||
mLog.error( "Couldn't set airspy gain to:" + gain.name(), e );
|
||||
|
||||
JOptionPane.showMessageDialog( mGain,
|
||||
"Couldn't set gain value to " + gain.getValue() );
|
||||
|
||||
mGain.setValue( mSelectedConfig.getGain().getValue() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mGain, "span,grow" );
|
||||
|
||||
/**
|
||||
* IF/VGA Gain
|
||||
*/
|
||||
mIFGainLabel = new JLabel( "IF Gain" );
|
||||
add( mIFGainLabel );
|
||||
mIFGainFillerLabel = new JLabel( "" );
|
||||
add( mIFGainFillerLabel, "span 2" );
|
||||
mIFGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getIFGain() ) );
|
||||
add( mIFGainValueLabel, "wrap" );
|
||||
|
||||
mIFGain = new JSlider( JSlider.HORIZONTAL,
|
||||
AirspyTunerController.IF_GAIN_MIN,
|
||||
AirspyTunerController.IF_GAIN_MAX,
|
||||
mSelectedConfig.getIFGain() );
|
||||
|
||||
mIFGain.setMajorTickSpacing( 1 );
|
||||
mIFGain.setPaintTicks( true );
|
||||
|
||||
mIFGain.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
int gain = mIFGain.getValue();
|
||||
|
||||
try
|
||||
{
|
||||
mController.setIFGain( gain );
|
||||
mSelectedConfig.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 );
|
||||
|
||||
mIFGain.setValue( mSelectedConfig.getIFGain() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mIFGain, "span,grow" );
|
||||
|
||||
/**
|
||||
* Mixer Gain
|
||||
*/
|
||||
mMixerGainLabel = new JLabel( "Mixer Gain" );
|
||||
add( mMixerGainLabel );
|
||||
|
||||
mMixerAGC = new JCheckBox( "AGC" );
|
||||
mMixerAGC.setSelected( mSelectedConfig.isMixerAGC() );
|
||||
mMixerAGC.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
try
|
||||
{
|
||||
mController.setMixerAGC( !mSelectedConfig.isMixerAGC() );
|
||||
mSelectedConfig.setMixerAGC( !mSelectedConfig.isMixerAGC() );
|
||||
save();
|
||||
}
|
||||
catch ( Exception e1 )
|
||||
{
|
||||
mLog.error( "Error setting Mixer AGC Enabled:" + !mSelectedConfig.isMixerAGC() );
|
||||
mMixerAGC.setSelected( mSelectedConfig.isMixerAGC() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
|
||||
add( mMixerAGC, "span 2,center" );
|
||||
|
||||
mMixerGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getMixerGain() ) );
|
||||
add( mMixerGainValueLabel, "wrap" );
|
||||
|
||||
mMixerGain = new JSlider( JSlider.HORIZONTAL,
|
||||
AirspyTunerController.MIXER_GAIN_MIN,
|
||||
AirspyTunerController.MIXER_GAIN_MAX,
|
||||
mSelectedConfig.getMixerGain() );
|
||||
|
||||
mMixerGain.setMajorTickSpacing( 1 );
|
||||
mMixerGain.setPaintTicks( true );
|
||||
|
||||
mMixerGain.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
int gain = mMixerGain.getValue();
|
||||
|
||||
try
|
||||
{
|
||||
mController.setMixerGain( gain );
|
||||
mSelectedConfig.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 );
|
||||
|
||||
mMixerGain.setValue( mSelectedConfig.getMixerGain() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mMixerGain, "span,grow" );
|
||||
|
||||
/**
|
||||
* LNA Gain
|
||||
*/
|
||||
mLNAGainLabel = new JLabel( "LNA Gain" );
|
||||
add( mLNAGainLabel );
|
||||
|
||||
mLNAAGC = new JCheckBox( "AGC" );
|
||||
mLNAAGC.setSelected( mSelectedConfig.isLNAAGC() );
|
||||
mLNAAGC.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
try
|
||||
{
|
||||
mController.setLNAAGC( !mSelectedConfig.isLNAAGC() );
|
||||
mSelectedConfig.setLNAAGC( !mSelectedConfig.isLNAAGC() );
|
||||
save();
|
||||
}
|
||||
catch ( Exception e1 )
|
||||
{
|
||||
mLog.error( "Error setting LNA AGC Enabled:" + !mSelectedConfig.isLNAAGC() );
|
||||
mLNAAGC.setSelected( mSelectedConfig.isLNAAGC() );
|
||||
}
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
add( mLNAAGC, "span 2,center" );
|
||||
|
||||
mLNAGainValueLabel = new JLabel( String.valueOf( mSelectedConfig.getLNAGain() ) );
|
||||
add( mLNAGainValueLabel, "wrap" );
|
||||
|
||||
mLNAGain = new JSlider( JSlider.HORIZONTAL,
|
||||
AirspyTunerController.LNA_GAIN_MIN,
|
||||
AirspyTunerController.LNA_GAIN_MAX,
|
||||
mSelectedConfig.getLNAGain() );
|
||||
|
||||
mLNAGain.setMajorTickSpacing( 1 );
|
||||
mLNAGain.setPaintTicks( true );
|
||||
|
||||
mLNAGain.addChangeListener( new ChangeListener()
|
||||
{
|
||||
@Override
|
||||
public void stateChanged( ChangeEvent event )
|
||||
{
|
||||
int gain = mLNAGain.getValue();
|
||||
|
||||
try
|
||||
{
|
||||
mController.setLNAGain( gain );
|
||||
mSelectedConfig.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 );
|
||||
|
||||
mLNAGain.setValue( mSelectedConfig.getLNAGain() );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mLNAGain, "span,grow" );
|
||||
|
||||
//Set the enabled state of each of the gain controls
|
||||
updateGainComponents( (GainMode)mGainModeCombo.getSelectedItem() );
|
||||
|
||||
/**
|
||||
* Create a new configuration
|
||||
*/
|
||||
mNewConfiguration = new JButton( "New" );
|
||||
|
||||
mNewConfiguration.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
// TunerConfiguration config =
|
||||
// mSettingsManager.addNewTunerConfiguration(
|
||||
// TunerType.AIRSPY_R820T,
|
||||
// "New Configuration" );
|
||||
//
|
||||
// mComboConfigurations.setModel( getModel() );
|
||||
//
|
||||
// mComboConfigurations.setSelectedItem( config );
|
||||
//
|
||||
// repaint();
|
||||
}
|
||||
} );
|
||||
|
||||
add( mNewConfiguration, "span 2,growx,push" );
|
||||
|
||||
/**
|
||||
* Delete the currently selected configuration
|
||||
*/
|
||||
mDeleteConfiguration = new JButton( "Delete" );
|
||||
|
||||
mDeleteConfiguration.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
AirspyTunerConfiguration selected =
|
||||
(AirspyTunerConfiguration)mComboConfigurations
|
||||
.getItemAt( mComboConfigurations.getSelectedIndex() );
|
||||
|
||||
if( selected != null )
|
||||
{
|
||||
int n = JOptionPane.showConfirmDialog(
|
||||
AirspyTunerConfigurationPanel.this,
|
||||
"Are you sure you want to delete '"
|
||||
+ selected.getName() + "'?",
|
||||
"Are you sure?",
|
||||
JOptionPane.YES_NO_OPTION );
|
||||
|
||||
if( n == JOptionPane.YES_OPTION )
|
||||
{
|
||||
// mSettingsManager.deleteTunerConfiguration( selected );
|
||||
|
||||
mComboConfigurations.setModel( getModel() );
|
||||
|
||||
mComboConfigurations.setSelectedIndex( 0 );
|
||||
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
||||
add( mDeleteConfiguration, "span 2,growx,push" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the enabled state of each of the gain controls according to the
|
||||
* specified gain mode. The overall gain controls are enabled for linearity
|
||||
* and sensitivity and the individual gain controls are disabled, and
|
||||
* vice-versa for custom mode.
|
||||
*/
|
||||
private void updateGainComponents( GainMode mode )
|
||||
{
|
||||
boolean isCustom = ( mode == GainMode.CUSTOM );
|
||||
|
||||
mGainLabel.setEnabled( !isCustom );
|
||||
mGainFillerLabel.setEnabled( !isCustom );
|
||||
mGainValueLabel.setEnabled( !isCustom );
|
||||
mGain.setEnabled( !isCustom );
|
||||
mIFGainLabel.setEnabled( isCustom );
|
||||
mIFGainFillerLabel.setEnabled( isCustom );
|
||||
mIFGainValueLabel.setEnabled( isCustom );
|
||||
mIFGain.setEnabled( isCustom );
|
||||
mLNAGainLabel.setEnabled( isCustom );
|
||||
mLNAAGC.setEnabled( isCustom );
|
||||
mLNAGainValueLabel.setEnabled( isCustom );
|
||||
mLNAGain.setEnabled( isCustom );
|
||||
mMixerGainValueLabel.setEnabled( isCustom );
|
||||
mMixerAGC.setEnabled( isCustom );
|
||||
mMixerGainLabel.setEnabled( isCustom );
|
||||
mMixerGain.setEnabled( isCustom );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates gui controls with the values from the tuner configuration
|
||||
* @param config - tuner configuration
|
||||
*/
|
||||
private void update( AirspyTunerConfiguration config )
|
||||
{
|
||||
mSelectedConfig = config;
|
||||
|
||||
try
|
||||
{
|
||||
mController.apply( config );
|
||||
|
||||
mName.setText( config.getName() );
|
||||
|
||||
AirspySampleRate rate = mController.getSampleRate(
|
||||
mSelectedConfig.getSampleRate() );
|
||||
|
||||
if( rate != null )
|
||||
{
|
||||
mSampleRateCombo.setSelectedItem( rate );
|
||||
}
|
||||
|
||||
mIFGain.setValue( mSelectedConfig.getIFGain() );
|
||||
mMixerGain.setValue( mSelectedConfig.getMixerGain() );
|
||||
mLNAGain.setValue( mSelectedConfig.getLNAGain() );
|
||||
|
||||
mMixerAGC.setSelected( mSelectedConfig.isMixerAGC() );
|
||||
mLNAAGC.setSelected( mSelectedConfig.isLNAAGC() );
|
||||
|
||||
Gain gain = mSelectedConfig.getGain();
|
||||
mGainModeCombo.setSelectedItem( Gain.getGainMode( gain ) );
|
||||
mGain.setValue( gain.getValue() );
|
||||
|
||||
// mSettingsManager.setSelectedTunerConfiguration(
|
||||
// TunerType.AIRSPY_R820T, mController.getDeviceInfo().getSerialNumber(), config );
|
||||
|
||||
save();
|
||||
}
|
||||
catch ( /*UsbException |*/ SourceException e1 )
|
||||
{
|
||||
JOptionPane.showMessageDialog(
|
||||
AirspyTunerConfigurationPanel.this,
|
||||
"Airspy Tuner Controller - couldn't "
|
||||
+ "apply the tuner configuration settings - " +
|
||||
e1.getLocalizedMessage() );
|
||||
|
||||
mLog.error( "Airspy Tuner Controller - couldn't apply "
|
||||
+ "config [" + config.getName() + "]", e1 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a combo box model for the tuner configuration combo component
|
||||
* by assembling a list of the tuner configurations appropriate for this
|
||||
* specific tuner
|
||||
*/
|
||||
private ComboBoxModel<AirspyTunerConfiguration> getModel()
|
||||
{
|
||||
// ArrayList<TunerConfiguration> configs =
|
||||
// mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T );
|
||||
|
||||
DefaultComboBoxModel<AirspyTunerConfiguration> model =
|
||||
new DefaultComboBoxModel<AirspyTunerConfiguration>();
|
||||
|
||||
// for( TunerConfiguration config: configs )
|
||||
// {
|
||||
// model.addElement( (AirspyTunerConfiguration)config );
|
||||
// }
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a specific (named) tuner configuration
|
||||
*/
|
||||
private AirspyTunerConfiguration getNamedConfiguration( String name )
|
||||
{
|
||||
// ArrayList<TunerConfiguration> configs =
|
||||
// mSettingsManager.getTunerConfigurations( TunerType.AIRSPY_R820T );
|
||||
|
||||
// for( TunerConfiguration config: configs )
|
||||
// {
|
||||
// if( config.getName().contentEquals( name ) )
|
||||
// {
|
||||
// return (AirspyTunerConfiguration)config;
|
||||
// }
|
||||
// }
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves current tuner configuration settings
|
||||
*/
|
||||
private void save()
|
||||
{
|
||||
// mSettingsManager.save();
|
||||
}
|
||||
}
|
||||
720
src/source/tuner/airspy/AirspyTunerEditor.java
Normal file
720
src/source/tuner/airspy/AirspyTunerEditor.java
Normal file
@@ -0,0 +1,720 @@
|
||||
/*******************************************************************************
|
||||
* 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.TunerConfigurationModel;
|
||||
|
||||
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" );
|
||||
|
||||
// tabs.add( "Info", new AirspyInformationPanel( mController ) );
|
||||
|
||||
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 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() );
|
||||
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() );
|
||||
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 );
|
||||
|
||||
config.setGain( Gain.getGain( (GainMode)mGainModeCombo.getSelectedItem(), mMasterGain.getValue() ) );
|
||||
config.setIFGain( mIFGain.getValue() );
|
||||
config.setMixerGain( mMixerGain.getValue() );
|
||||
config.setLNAGain( mLNAGain.getValue() );
|
||||
config.setMixerAGC( mMixerAGC.isSelected() );
|
||||
config.setLNAAGC( mLNAAGC.isSelected() );
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
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 );
|
||||
}
|
||||
else
|
||||
{
|
||||
setControlsEnabled( false );
|
||||
mConfigurationName.setText( hasItem() ? getItem().getName() : "" );
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
/*******************************************************************************
|
||||
* SDR Trunk
|
||||
* Copyright (C) 2015 Dennis Sheirer
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
******************************************************************************/
|
||||
package source.tuner.airspy;
|
||||
|
||||
import gui.control.JFrequencyControl;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import settings.SettingsManager;
|
||||
import source.SourceException;
|
||||
import source.tuner.frequency.FrequencyChangeEvent;
|
||||
import source.tuner.frequency.FrequencyChangeEvent.Event;
|
||||
import source.tuner.frequency.IFrequencyChangeProcessor;
|
||||
|
||||
import com.jidesoft.swing.JideTabbedPane;
|
||||
|
||||
public class AirspyTunerEditorPanel extends JPanel
|
||||
implements IFrequencyChangeProcessor
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final static Logger mLog =
|
||||
LoggerFactory.getLogger( AirspyTunerEditorPanel.class );
|
||||
|
||||
private JFrequencyControl mFrequencyControl;
|
||||
|
||||
private AirspyTuner mTuner;
|
||||
private AirspyTunerController mController;
|
||||
private SettingsManager mSettingsManager;
|
||||
|
||||
public AirspyTunerEditorPanel( AirspyTuner tuner,
|
||||
SettingsManager settingsManager )
|
||||
{
|
||||
mTuner = tuner;
|
||||
mController = mTuner.getController();
|
||||
mSettingsManager = settingsManager;
|
||||
|
||||
initGUI();
|
||||
}
|
||||
|
||||
private void initGUI()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 2",
|
||||
"[right,grow][grow]",
|
||||
"[][][][][][grow]" ) );
|
||||
|
||||
add( new JLabel( "Airspy Tuner" ), "span,align center" );
|
||||
|
||||
mFrequencyControl = new JFrequencyControl();
|
||||
|
||||
mFrequencyControl.addListener( this );
|
||||
|
||||
/* Add frequency control as frequency change listener. This creates a
|
||||
* feedback loop, so the control does not rebroadcast the event */
|
||||
mTuner.getTunerController().addListener( mFrequencyControl );
|
||||
|
||||
mFrequencyControl.setFrequency( mController.getFrequency(), false );
|
||||
|
||||
add( mFrequencyControl, "span,align center" );
|
||||
|
||||
JideTabbedPane tabs = new JideTabbedPane();
|
||||
tabs.setFont( this.getFont() );
|
||||
tabs.setForeground( Color.BLACK );
|
||||
|
||||
add( tabs, "span,grow,push" );
|
||||
|
||||
tabs.add( "Config", new AirspyTunerConfigurationPanel(
|
||||
mSettingsManager, mTuner.getController() ) );
|
||||
|
||||
tabs.add( "Info", new AirspyInformationPanel( mTuner.getController() ) );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void frequencyChanged( FrequencyChangeEvent event )
|
||||
{
|
||||
if( event.getEvent() == Event.NOTIFICATION_FREQUENCY_CHANGE )
|
||||
{
|
||||
try
|
||||
{
|
||||
mController.setFrequency( event.getValue().longValue() );
|
||||
}
|
||||
catch ( SourceException e )
|
||||
{
|
||||
mLog.error( "error setting frequency [" +
|
||||
event.getValue().longValue() + "]", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package source.tuner.configuration;
|
||||
|
||||
import gui.editor.Editor;
|
||||
import source.tuner.TunerType;
|
||||
|
||||
public abstract class TunerConfigurationEditor extends Editor<TunerConfiguration>
|
||||
{
|
||||
@@ -14,8 +13,6 @@ public abstract class TunerConfigurationEditor extends Editor<TunerConfiguration
|
||||
mTunerConfigurationModel = model;
|
||||
}
|
||||
|
||||
public abstract boolean canEdit( TunerType tunerType );
|
||||
|
||||
public TunerConfigurationModel getTunerConfigurationModel()
|
||||
{
|
||||
return mTunerConfigurationModel;
|
||||
|
||||
@@ -4,7 +4,9 @@ import gui.editor.Editor;
|
||||
import gui.editor.EmptyEditor;
|
||||
import source.tuner.Tuner;
|
||||
import source.tuner.TunerType;
|
||||
import source.tuner.airspy.AirspyTuner;
|
||||
import source.tuner.airspy.AirspyTunerConfiguration;
|
||||
import source.tuner.airspy.AirspyTunerEditor;
|
||||
import source.tuner.fcd.proV1.FCD1TunerConfiguration;
|
||||
import source.tuner.fcd.proplusV2.FCD2TunerConfiguration;
|
||||
import source.tuner.hackrf.HackRFTunerConfiguration;
|
||||
@@ -45,7 +47,7 @@ public class TunerConfigurationFactory
|
||||
switch( tuner.getTunerType() )
|
||||
{
|
||||
case AIRSPY_R820T:
|
||||
break;
|
||||
return new AirspyTunerEditor( model, (AirspyTuner)tuner );
|
||||
case ELONICS_E4000:
|
||||
break;
|
||||
case FUNCUBE_DONGLE_PRO:
|
||||
|
||||
@@ -23,6 +23,7 @@ import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
@@ -43,13 +44,15 @@ import org.slf4j.LoggerFactory;
|
||||
import org.usb4java.LibUsbException;
|
||||
|
||||
import source.SourceException;
|
||||
import source.tuner.TunerType;
|
||||
import source.tuner.airspy.AirspyDeviceInformation;
|
||||
import source.tuner.airspy.AirspyTunerEditor;
|
||||
import source.tuner.configuration.TunerConfiguration;
|
||||
import source.tuner.configuration.TunerConfigurationEditor;
|
||||
import source.tuner.configuration.TunerConfigurationEvent;
|
||||
import source.tuner.configuration.TunerConfigurationEvent.Event;
|
||||
import source.tuner.configuration.TunerConfigurationModel;
|
||||
import source.tuner.rtl.RTL2832Tuner;
|
||||
import source.tuner.rtl.RTL2832TunerController.Descriptor;
|
||||
import source.tuner.rtl.RTL2832TunerController.SampleRate;
|
||||
import source.tuner.rtl.r820t.R820TTunerController.R820TGain;
|
||||
import source.tuner.rtl.r820t.R820TTunerController.R820TLNAGain;
|
||||
@@ -67,6 +70,7 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
private R820TTunerController mController;
|
||||
|
||||
private JTextField mConfigurationName;
|
||||
private JButton mTunerInfo;
|
||||
private JSpinner mFrequencyCorrection;
|
||||
private JComboBox<R820TGain> mComboMasterGain;
|
||||
private JComboBox<R820TMixerGain> mComboMixerGain;
|
||||
@@ -84,12 +88,6 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEdit( TunerType tunerType )
|
||||
{
|
||||
return tunerType == TunerType.RAFAELMICRO_R820T;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save()
|
||||
{
|
||||
@@ -169,6 +167,11 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
mConfigurationName.setEnabled( enabled );
|
||||
}
|
||||
|
||||
if( mTunerInfo.isEnabled() != enabled )
|
||||
{
|
||||
mTunerInfo.setEnabled( enabled );
|
||||
}
|
||||
|
||||
if( mFrequencyCorrection.isEnabled() != enabled )
|
||||
{
|
||||
mFrequencyCorrection.setEnabled( enabled );
|
||||
@@ -213,9 +216,9 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
private void init()
|
||||
{
|
||||
setLayout( new MigLayout( "fill,wrap 4", "[right][grow,fill][right][grow,fill]",
|
||||
"[][][][][][][grow]" ) );
|
||||
"[][][][][][][][grow]" ) );
|
||||
|
||||
add( new JLabel( "R820T Tuner Configuration" ), "span, align center" );
|
||||
add( new JLabel( "R820T Tuner Configuration" ), "span,align center" );
|
||||
|
||||
/**
|
||||
* Tuner Configuration Name
|
||||
@@ -234,7 +237,21 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
} );
|
||||
|
||||
add( new JLabel( "Name:" ) );
|
||||
add( mConfigurationName, "span,grow" );
|
||||
add( mConfigurationName, "span 2" );
|
||||
|
||||
mTunerInfo = new JButton( "Tuner Info" );
|
||||
mTunerInfo.setEnabled( false );
|
||||
mTunerInfo.addActionListener( new ActionListener()
|
||||
{
|
||||
@Override
|
||||
public void actionPerformed( ActionEvent e )
|
||||
{
|
||||
JOptionPane.showMessageDialog( R820TTunerEditor.this, getTunerInfo(),
|
||||
"Tuner Info", JOptionPane.INFORMATION_MESSAGE );
|
||||
}
|
||||
} );
|
||||
add( mTunerInfo );
|
||||
|
||||
|
||||
mComboSampleRate = new JComboBox<>( SampleRate.values() );
|
||||
mComboSampleRate.setEnabled( false );
|
||||
@@ -312,10 +329,12 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
add( mFrequencyCorrection );
|
||||
|
||||
add( new JSeparator( JSeparator.HORIZONTAL ), "span,grow" );
|
||||
|
||||
|
||||
/**
|
||||
* Gain Controls
|
||||
*/
|
||||
add( new JLabel( "Gain" ), "span" );
|
||||
|
||||
/* Master Gain Control */
|
||||
mComboMasterGain = new JComboBox<R820TGain>( R820TGain.values() );
|
||||
mComboMasterGain.setEnabled( false );
|
||||
@@ -500,4 +519,48 @@ public class R820TTunerEditor extends TunerConfigurationEditor
|
||||
add( new JLabel( "VGA:" ) );
|
||||
add( mComboVGAGain );
|
||||
}
|
||||
|
||||
private String getTunerInfo()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
Descriptor descriptor = mController.getDescriptor();
|
||||
|
||||
sb.append( "<html><h3>RTL-2832 with R820T Tuner</h3>" );
|
||||
|
||||
if( descriptor == null )
|
||||
{
|
||||
sb.append( "No EEPROM Descriptor Available" );
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.append( "<b>USB ID: </b>" );
|
||||
sb.append( descriptor.getVendorID() );
|
||||
sb.append( ":" );
|
||||
sb.append( descriptor.getProductID() );
|
||||
sb.append( "<br>" );
|
||||
|
||||
sb.append( "<b>Vendor: </b>" );
|
||||
sb.append( descriptor.getVendorLabel() );
|
||||
sb.append( "<br>" );
|
||||
|
||||
sb.append( "<b>Product: </b>" );
|
||||
sb.append( descriptor.getProductLabel() );
|
||||
sb.append( "<br>" );
|
||||
|
||||
sb.append( "<b>Serial: </b>" );
|
||||
sb.append( descriptor.getSerial() );
|
||||
sb.append( "<br>" );
|
||||
|
||||
sb.append( "<b>IR Enabled: </b>" );
|
||||
sb.append( descriptor.irEnabled() );
|
||||
sb.append( "<br>" );
|
||||
|
||||
sb.append( "<b>Remote Wake: </b>" );
|
||||
sb.append( descriptor.remoteWakeupEnabled() );
|
||||
sb.append( "<br>" );
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user