diff --git a/config/53-hackrf.rules b/config/53-hackrf.rules new file mode 100644 index 00000000..fcfa6d17 --- /dev/null +++ b/config/53-hackrf.rules @@ -0,0 +1,3 @@ +ATTR{idVendor}=="1d50", ATTR{idProduct}=="604b", SYMLINK+="hackrf-jawbreaker-%k", MODE="660", GROUP="plugdev" +ATTR{idVendor}=="1d50", ATTR{idProduct}=="6089", SYMLINK+="hackrf-one-%k", MODE="660", GROUP="plugdev" +ATTR{idVendor}=="1fc9", ATTR{idProduct}=="000c", SYMLINK+="nxp-dfu-%k", MODE="660", GROUP="plugdev" \ No newline at end of file diff --git a/imports/commons-io-2.4.jar b/imports/commons-io-2.4.jar new file mode 100644 index 00000000..90035a4f Binary files /dev/null and b/imports/commons-io-2.4.jar differ diff --git a/src/settings/IntegerSetting.java b/src/settings/IntegerSetting.java deleted file mode 100644 index f38541c5..00000000 --- a/src/settings/IntegerSetting.java +++ /dev/null @@ -1,30 +0,0 @@ -package settings; - -import javax.xml.bind.annotation.XmlAttribute; - -public class IntegerSetting extends Setting -{ - private int mValue; - - public IntegerSetting() - { - } - - public IntegerSetting( String name, int value ) - { - super( name ); - - mValue = value; - } - - @XmlAttribute - public int getValue() - { - return mValue; - } - - public void setValue( int value ) - { - mValue = value; - } -} diff --git a/src/settings/Settings.java b/src/settings/Settings.java index d70cb8c2..fc9e1783 100644 --- a/src/settings/Settings.java +++ b/src/settings/Settings.java @@ -33,6 +33,7 @@ import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; +import source.tuner.hackrf.HackRFTunerConfiguration; import source.tuner.rtl.e4k.E4KTunerConfiguration; import source.tuner.rtl.r820t.R820TTunerConfiguration; @@ -45,7 +46,7 @@ import source.tuner.rtl.r820t.R820TTunerConfiguration; FCD1TunerConfiguration.class, FCD2TunerConfiguration.class, FileSetting.class, - IntegerSetting.class, + HackRFTunerConfiguration.class, MapViewSetting.class, MapIcon.class, R820TTunerConfiguration.class, @@ -179,20 +180,6 @@ public class Settings return null; } - public IntegerSetting getIntegerSetting( String name ) - { - for( Setting setting: mSettings ) - { - if( setting instanceof IntegerSetting && - ((IntegerSetting)setting).getName().contentEquals( name ) ) - { - return (IntegerSetting)setting; - } - } - - return null; - } - public ColorSetting getColorSetting( ColorSettingName name ) { for( Setting setting: mSettings ) diff --git a/src/settings/SettingsManager.java b/src/settings/SettingsManager.java index 5dbe849d..475f98df 100644 --- a/src/settings/SettingsManager.java +++ b/src/settings/SettingsManager.java @@ -52,6 +52,7 @@ import source.tuner.TunerConfigurationAssignment; import source.tuner.TunerType; import source.tuner.fcd.proV1.FCD1TunerConfiguration; import source.tuner.fcd.proplusV2.FCD2TunerConfiguration; +import source.tuner.hackrf.HackRFTunerConfiguration; import source.tuner.rtl.e4k.E4KTunerConfiguration; import source.tuner.rtl.r820t.R820TTunerConfiguration; @@ -147,33 +148,6 @@ public class SettingsManager return mSettings.getSetting( name ); } - public IntegerSetting getIntegerSetting( String name, int defaultValue ) - { - IntegerSetting setting = mSettings.getIntegerSetting( name ); - - if( setting == null ) - { - setting = new IntegerSetting( name, defaultValue ); - - mSettings.addSetting( setting ); - - save(); - } - - return setting; - } - - public void setIntegerSetting( String name, int value ) - { - IntegerSetting setting = getIntegerSetting( name, value ); - - setting.setValue( value ); - - save(); - - broadcastSettingChange( setting ); - } - /** * Returns the current setting, or if the setting doesn't exist * returns a newly created setting with the specified parameters @@ -616,6 +590,15 @@ public class SettingsManager save(); return configPlus; + case HACKRF: + HackRFTunerConfiguration hackConfig = + new HackRFTunerConfiguration( name ); + + getSettings().addTunerConfiguration( hackConfig ); + + save(); + + return hackConfig; case RAFAELMICRO_R820T: R820TTunerConfiguration r820TConfig = new R820TTunerConfiguration( name ); diff --git a/src/source/tuner/Tuner.java b/src/source/tuner/Tuner.java index 71d3afcb..46392840 100644 --- a/src/source/tuner/Tuner.java +++ b/src/source/tuner/Tuner.java @@ -48,6 +48,11 @@ public abstract class Tuner implements FrequencyChangeBroadcaster, mName = name; } + public void setName( String name ) + { + mName = name; + } + /** * Return an editor panel for the tuner */ diff --git a/src/source/tuner/TunerClass.java b/src/source/tuner/TunerClass.java index dae3665b..d7339820 100644 --- a/src/source/tuner/TunerClass.java +++ b/src/source/tuner/TunerClass.java @@ -34,6 +34,7 @@ public enum TunerClass FUNCUBE_DONGLE_PRO_PLUS( TunerType.FUNCUBE_DONGLE_PRO_PLUS, "04D8", "FB31", "Hamlincrest", "Funcube Dongle Pro Plus" ), GIGABYTE_GTU7300( TunerType.FITIPOWER_FC0012, "1B80", "D393", "Gigabyte", "GT-U7300" ), GTEK_T803( TunerType.FITIPOWER_FC0012, "1F4D", "B803", "GTek", "T803" ), + HACKRF_ONE( TunerType.HACKRF, "1D50", "6089", "Great Scott Gadgets", "HackRF One" ), LIFEVIEW_LV5T_DELUXE( TunerType.FITIPOWER_FC0012, "1F4D", "C803", "Liveview", "LV5T Deluxe" ), MYGICA_TD312( TunerType.FITIPOWER_FC0012, "1F4D", "D286", "MyGica", "TD312" ), PEAK_102569AGPK( TunerType.FITIPOWER_FC0012, "1B80", "D395", "Peak", "102569AGPK" ), @@ -174,6 +175,12 @@ public enum TunerClass retVal = DEXATEK_5217_DVBT; } break; + case 7504: //1D50 + if( productID == 24713 ) //6089 + { + retVal = HACKRF_ONE; + } + break; case 8013: //1F4D if( productID == 47107 ) //B803 { diff --git a/src/source/tuner/TunerManager.java b/src/source/tuner/TunerManager.java index 0107b681..0693e72b 100644 --- a/src/source/tuner/TunerManager.java +++ b/src/source/tuner/TunerManager.java @@ -40,6 +40,8 @@ import source.tuner.ettus.B100Tuner; import source.tuner.fcd.FCDTuner; import source.tuner.fcd.proV1.FCD1TunerController; import source.tuner.fcd.proplusV2.FCD2TunerController; +import source.tuner.hackrf.HackRFTuner; +import source.tuner.hackrf.HackRFTunerController; import source.tuner.rtl.RTL2832Tuner; import source.tuner.rtl.RTL2832TunerController; import source.tuner.rtl.e4k.E4KTunerController; @@ -361,6 +363,29 @@ public class TunerManager status = sLOADED; reason = null; break; + case HACKRF_ONE: +// try +// { +// HackRFTunerController hackRFController = +// new HackRFTunerController( device ); +// +// hackRFController.init(); +// +// HackRFTuner hackRFTuner = +// new HackRFTuner( hackRFController ); +// +// mTuners.add( hackRFTuner ); +// name = hackRFTuner.getName(); +// status = sLOADED; +// reason = null; +// } +// catch( SourceException se ) +// { +// status = sNOT_LOADED; +// reason = "Error constructing HackRF tuner " +// + "controller - " + se.getLocalizedMessage(); +// } + break; case UNKNOWN: default: break; diff --git a/src/source/tuner/TunerNode.java b/src/source/tuner/TunerNode.java index cbcf1b89..20f1dfe4 100644 --- a/src/source/tuner/TunerNode.java +++ b/src/source/tuner/TunerNode.java @@ -134,6 +134,7 @@ public class TunerNode extends BaseNode implements FrequencyChangeListener mFrequency = frequency; getModel().nodeChanged( TunerNode.this ); } + break; case SAMPLE_RATE: int sampleRate = (int)event.getValue(); diff --git a/src/source/tuner/TunerType.java b/src/source/tuner/TunerType.java index 0714eb53..0245649a 100644 --- a/src/source/tuner/TunerType.java +++ b/src/source/tuner/TunerType.java @@ -27,6 +27,7 @@ public enum TunerType FITIPOWER_FC0013( "FC0013" ), FUNCUBE_DONGLE_PRO( "Funcube Dongle Pro" ), FUNCUBE_DONGLE_PRO_PLUS( "Funcube Dongle Pro Plus" ), + HACKRF( "HackRF" ), RAFAELMICRO_R820T( "R820T" ), RAFAELMICRO_R828D( "R828D" ), RTL2832_VARIOUS( "Generic" ), diff --git a/src/source/tuner/hackrf/HackRFInformationPanel.java b/src/source/tuner/hackrf/HackRFInformationPanel.java new file mode 100644 index 00000000..4a7ad795 --- /dev/null +++ b/src/source/tuner/hackrf/HackRFInformationPanel.java @@ -0,0 +1,86 @@ +package source.tuner.hackrf; + +import javax.swing.JLabel; +import javax.swing.JPanel; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.tuner.hackrf.HackRFTunerController.Serial; + +public class HackRFInformationPanel extends JPanel +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = + LoggerFactory.getLogger( HackRFInformationPanel.class ); + + private HackRFTunerController mController; + + /** + * Displays HackRF firmware, serial number and part identifier + */ + public HackRFInformationPanel( HackRFTunerController controller ) + { + mController = controller; + + initGUI(); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", + "[right,grow][grow]", + "[][][][grow]" ) ); + + Serial serial = null; + + try + { + serial = mController.getSerial(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF serial number", e ); + } + + if( serial != null ) + { + add( new JLabel( "Serial:" ) ); + add( new JLabel( serial.getSerialNumber() ) ); + add( new JLabel( "Part:" ) ); + add( new JLabel( serial.getPartID() ) ); + } + else + { + add( new JLabel( "Serial:" ) ); + add( new JLabel( "Unknown" ) ); + add( new JLabel( "Part:" ) ); + add( new JLabel( "Unknown" ) ); + } + + String firmware = null; + + try + { + firmware = mController.getFirmwareVersion(); + } + catch( Exception e ) + { + mLog.error( "couldn't read HackRF firmware version", e ); + } + + add( new JLabel( "Firmware:" ) ); + + if( firmware != null ) + { + add( new JLabel( firmware ) ); + } + else + { + add( new JLabel( "Unknown" ) ); + } + } +} diff --git a/src/source/tuner/hackrf/HackRFTuner.java b/src/source/tuner/hackrf/HackRFTuner.java new file mode 100644 index 00000000..c7204b54 --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTuner.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package source.tuner.hackrf; + +import java.util.concurrent.RejectedExecutionException; + +import javax.swing.JPanel; +import javax.usb.UsbException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sample.Listener; +import source.SourceException; +import source.tuner.Tuner; +import source.tuner.TunerChannel; +import source.tuner.TunerChannelSource; +import source.tuner.TunerClass; +import source.tuner.TunerConfiguration; +import source.tuner.TunerType; +import source.tuner.hackrf.HackRFTunerController.BoardID; +import source.tuner.hackrf.HackRFTunerController.Serial; +import controller.ResourceManager; +import controller.ThreadPoolManager; + +public class HackRFTuner extends Tuner +{ + private final static Logger mLog = LoggerFactory.getLogger( HackRFTuner.class ); + + private HackRFTunerController mController; + + public HackRFTuner( HackRFTunerController controller ) throws SourceException + { + super( "HackRF" ); + + mController = controller; + + /* Register for frequency/sample rate changes */ + mController.addListener( this ); + + try + { + BoardID id = mController.getBoardID(); + super.setName( id.getLabel() ); + + String version = mController.getFirmwareVersion(); + mLog.info( "HackRF firmware: " + version ); + + Serial serial = mController.getSerial(); + mLog.info( "HackRF Part ID: " + serial.getPartID() ); + mLog.info( "HackRF Serial Number: " + serial.getSerialNumber() ); + } + catch( UsbException e ) + { + mLog.error( "couldn't read HackRF board id", e ); + } + } + + public HackRFTunerController getController() + { + return mController; + } + + @Override + public TunerClass getTunerClass() + { + return TunerClass.HACKRF_ONE; + } + + @Override + public TunerType getTunerType() + { + return TunerType.HACKRF; + } + + @Override + public JPanel getEditor( ResourceManager resourceManager ) + { + return new HackRFTunerEditorPanel( this, resourceManager ); + } + + @Override + public void apply( TunerConfiguration config ) throws SourceException + { + mLog.error( "B100 Tuner - can't apply tuner configuration" ); + } + + @Override + public int getSampleRate() + { + return mController.getSampleRate(); + } + + @Override + public long getFrequency() throws SourceException + { + return mController.getFrequency(); + } + + @Override + public TunerChannelSource getChannel( ThreadPoolManager threadPoolManager, + TunerChannel channel ) throws RejectedExecutionException, SourceException + { + return mController.getChannel( threadPoolManager, this, channel ); + } + + @Override + public void releaseChannel( TunerChannelSource source ) + { + /* Unregister for receiving samples */ + removeListener( (Listener)source ); + + /* Tell the controller to release the channel and cleanup */ + if( source != null ) + { + mController.releaseChannel( source ); + } + } + + @Override + public String getUniqueID() + { + try + { + return mController.getSerial().getSerialNumber(); + } + catch( Exception e ) + { + mLog.error( "error gettting serial number", e ); + } + + return BoardID.HACKRF_ONE.getLabel(); + } + + @Override + public void addListener( Listener listener ) + { + mController.addListener( listener ); + } + + @Override + public void removeListener( Listener listener ) + { + mController.removeListener( listener ); + } +} diff --git a/src/source/tuner/hackrf/HackRFTunerConfiguration.java b/src/source/tuner/hackrf/HackRFTunerConfiguration.java new file mode 100644 index 00000000..c3d5736f --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTunerConfiguration.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package source.tuner.hackrf; + + +import javax.xml.bind.annotation.XmlAttribute; + +import source.tuner.TunerConfiguration; +import source.tuner.TunerType; +import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; +import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; +import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; + +public class HackRFTunerConfiguration extends TunerConfiguration +{ + private HackRFSampleRate mSampleRate = HackRFSampleRate.RATE2_016MHZ; + private HackRFLNAGain mLNAGain = HackRFLNAGain.GAIN_0; + private HackRFVGAGain mVGAGain = HackRFVGAGain.GAIN_10; + private double mFrequencyCorrection = 0.0d; + + /** + * Default constructor for JAXB + */ + public HackRFTunerConfiguration() + { + this( "Default" ); + } + + public HackRFTunerConfiguration( String name ) + { + super( name ); + } + + @Override + public TunerType getTunerType() + { + return TunerType.HACKRF; + } + + @XmlAttribute( name = "lna_gain" ) + public HackRFLNAGain getLNAGain() + { + return mLNAGain; + } + + public void setLNAGain( HackRFLNAGain lnaGain ) + { + mLNAGain = lnaGain; + } + + @XmlAttribute( name = "vga_gain" ) + public HackRFVGAGain getVGAGain() + { + return mVGAGain; + } + + public void setVGAGain( HackRFVGAGain vgaGain ) + { + mVGAGain = vgaGain; + } + + @XmlAttribute( name = "frequency_correction" ) + public double getFrequencyCorrection() + { + return mFrequencyCorrection; + } + + public void setFrequencyCorrection( double value ) + { + mFrequencyCorrection = value; + } + + @XmlAttribute( name = "sample_rate" ) + public HackRFSampleRate getSampleRate() + { + return mSampleRate; + } + + public void setSampleRate( HackRFSampleRate sampleRate ) + { + mSampleRate = sampleRate; + } +} diff --git a/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java new file mode 100644 index 00000000..462321bc --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTunerConfigurationPanel.java @@ -0,0 +1,529 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package source.tuner.hackrf; + +import java.awt.EventQueue; +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.ArrayList; + +import javax.swing.BorderFactory; +import javax.swing.ComboBoxModel; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +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 source.SourceException; +import source.tuner.TunerConfiguration; +import source.tuner.TunerConfigurationAssignment; +import source.tuner.TunerType; +import source.tuner.hackrf.HackRFTunerController.HackRFLNAGain; +import source.tuner.hackrf.HackRFTunerController.HackRFSampleRate; +import source.tuner.hackrf.HackRFTunerController.HackRFVGAGain; +import controller.ResourceManager; + +public class HackRFTunerConfigurationPanel extends JPanel +{ + private final static Logger mLog = + LoggerFactory.getLogger( HackRFTunerConfigurationPanel.class ); + + private static final long serialVersionUID = 1L; + + private ResourceManager mResourceManager; + private HackRFTunerController mController; + private HackRFTunerConfiguration mSelectedConfig; + + private JButton mNewConfiguration; + private JButton mDeleteConfiguration; + private JComboBox mComboConfigurations; + + private JTextField mName; + + private JSpinner mFrequencyCorrection; + private JComboBox mComboLNAGain; + private JComboBox mComboVGAGain; + + private JComboBox mComboSampleRate; + + public HackRFTunerConfigurationPanel( ResourceManager resourceManager, + HackRFTunerController controller ) + { + mResourceManager = resourceManager; + mController = controller; + + init(); + } + + /** + * Initializes the gui components + */ + private void init() + { + setLayout( new MigLayout( "fill,wrap 2", "[grow,right][grow]", "[][][][][][][grow]" ) ); + + /** + * Tuner configuration selector + */ + mComboConfigurations = new JComboBox(); + + mComboConfigurations.setModel( getModel() ); + + /* Determine which tuner configuration should be selected/displayed */ + TunerConfigurationAssignment savedConfig = null; + String serial = null; + + try + { + serial = mController.getSerial().getSerialNumber(); + } + catch ( UsbException e2 ) + { + mLog.error( "couldn't read hackrf serial number", e2 ); + } + + if( serial != null ) + { + savedConfig = mResourceManager + .getSettingsManager().getSelectedTunerConfiguration( + TunerType.HACKRF, 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 ) + { + mResourceManager.getSettingsManager() + .setSelectedTunerConfiguration( TunerType.HACKRF, + serial, mSelectedConfig ); + } + } + + mComboConfigurations.setSelectedItem( mSelectedConfig ); + + mComboConfigurations.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + HackRFTunerConfiguration selected = + (HackRFTunerConfiguration)mComboConfigurations + .getItemAt( mComboConfigurations.getSelectedIndex() ); + + if( selected != null ) + { + update( selected ); + } + } + }); + + add( new JLabel( "Config:" ) ); + add( mComboConfigurations, "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, "growx,push" ); + + /** + * Frequency Correction + */ + SpinnerModel model = + new SpinnerNumberModel( 0.0, //initial value + -1000.0, //min + 1000.0, //max + 0.1 ); //step + + mFrequencyCorrection = new JSpinner( model ); + + JSpinner.NumberEditor editor = + (JSpinner.NumberEditor)mFrequencyCorrection.getEditor(); + + DecimalFormat format = editor.getFormat(); + format.setMinimumFractionDigits( 1 ); + editor.getTextField().setHorizontalAlignment( SwingConstants.CENTER ); + + mFrequencyCorrection.setValue( mSelectedConfig.getFrequencyCorrection() ); + + mFrequencyCorrection.addChangeListener( new ChangeListener() + { + @Override + public void stateChanged( ChangeEvent e ) + { + final double value = ((SpinnerNumberModel)mFrequencyCorrection + .getModel()).getNumber().doubleValue(); + + EventQueue.invokeLater( new Runnable() + { + @Override + public void run() + { + try + { + mController.setFrequencyCorrection( value ); + mSelectedConfig.setFrequencyCorrection( value ); + save(); + } + catch ( SourceException e1 ) + { + JOptionPane.showMessageDialog( + HackRFTunerConfigurationPanel.this, + "HackRF Tuner Controller - couldn't apply " + + "frequency correction value: " + value + + e1.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply " + + "frequency correction value: " + value, e1 ); + } + } + } ); + } + } ); + + add( new JLabel( "Correction PPM:" ) ); + add( mFrequencyCorrection, "growx,push" ); + + /** + * Sample Rate + */ + mComboSampleRate = new JComboBox<>( HackRFSampleRate.values() ); + mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); + mComboSampleRate.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + HackRFSampleRate sampleRate = + (HackRFSampleRate)mComboSampleRate.getSelectedItem(); + try + { + + mController.setSampleRate( sampleRate ); + + mSelectedConfig.setSampleRate( sampleRate ); + save(); + } + catch ( UsbException e2 ) + { + JOptionPane.showMessageDialog( + HackRFTunerConfigurationPanel.this, + "HackRF Tuner Controller - couldn't apply the sample " + + "rate setting [" + sampleRate.getLabel() + "] " + + e2.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply sample " + + "rate setting [" + sampleRate.getLabel() + "]", e ); + } + } + } ); + add( new JLabel( "Sample Rate:" ) ); + add( mComboSampleRate, "growx,push" ); + + /** + * Gain Controls + */ + JPanel gainPanel = new JPanel(); + gainPanel.setLayout( new MigLayout( "", "[grow,fill]", "[grow,fill]" ) ); + gainPanel.setBorder( BorderFactory.createTitledBorder( "Gain" ) ); + + /* LNA Gain Control */ + mComboLNAGain = new JComboBox( HackRFLNAGain.values() ); + mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); + mComboLNAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + HackRFLNAGain lnaGain = + (HackRFLNAGain)mComboLNAGain.getSelectedItem(); + + if ( lnaGain == null ) + { + lnaGain = HackRFLNAGain.GAIN_0; + } + + if( mComboLNAGain.isEnabled() ) + { + mController.setLNAGain( lnaGain ); + } + + mSelectedConfig.setLNAGain( lnaGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( + HackRFTunerConfigurationPanel.this, + "HackRF Tuner Controller - couldn't apply the LNA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply LNA " + + "gain setting - ", e ); + } + } + } ); + mComboLNAGain.setToolTipText( "LNA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + gainPanel.add( new JLabel( "LNA" ) ); + gainPanel.add( mComboLNAGain ); + + /* VGA Gain Control */ + mComboVGAGain = new JComboBox( HackRFVGAGain.values() ); + mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); + mComboVGAGain.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent arg0 ) + { + try + { + HackRFVGAGain vgaGain = (HackRFVGAGain)mComboVGAGain.getSelectedItem(); + + if( vgaGain == null ) + { + vgaGain = HackRFVGAGain.GAIN_4; + } + + if( mComboVGAGain.isEnabled() ) + { + mController.setVGAGain( vgaGain ); + } + + mSelectedConfig.setVGAGain( vgaGain ); + save(); + } + catch ( UsbException e ) + { + JOptionPane.showMessageDialog( + HackRFTunerConfigurationPanel.this, + "HackRF Tuner Controller - couldn't apply the VGA " + + "gain setting - " + e.getLocalizedMessage() ); + + mLog.error( "HackRF Tuner Controller - couldn't apply VGA " + + "gain setting", e ); + } + } + } ); + mComboVGAGain.setToolTipText( "VGA Gain. Set master gain " + + "to MANUAL to enable adjustment" ); + gainPanel.add( new JLabel( "VGA" ) ); + gainPanel.add( mComboVGAGain, "wrap" ); + + add( gainPanel, "span" ); + + /** + * Create a new configuration + */ + mNewConfiguration = new JButton( "New" ); + + mNewConfiguration.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + TunerConfiguration config = + mResourceManager.getSettingsManager() + .addNewTunerConfiguration( + TunerType.HACKRF, + "New Configuration" ); + + mComboConfigurations.setModel( getModel() ); + + mComboConfigurations.setSelectedItem( config ); + + repaint(); + } + } ); + + add( mNewConfiguration, "growx,push" ); + + /** + * Delete the currently selected configuration + */ + mDeleteConfiguration = new JButton( "Delete" ); + + mDeleteConfiguration.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( ActionEvent e ) + { + HackRFTunerConfiguration selected = + (HackRFTunerConfiguration)mComboConfigurations + .getItemAt( mComboConfigurations.getSelectedIndex() ); + + if( selected != null ) + { + int n = JOptionPane.showConfirmDialog( + HackRFTunerConfigurationPanel.this, + "Are you sure you want to delete '" + + selected.getName() + "'?", + "Are you sure?", + JOptionPane.YES_NO_OPTION ); + + if( n == JOptionPane.YES_OPTION ) + { + mResourceManager.getSettingsManager() + .deleteTunerConfiguration( selected ); + + mComboConfigurations.setModel( getModel() ); + + mComboConfigurations.setSelectedIndex( 0 ); + + repaint(); + } + } + } + } ); + + add( mDeleteConfiguration, "growx,push,wrap" ); + } + + /** + * Updates gui controls with the values from the tuner configuration + * @param config - tuner configuration + */ + private void update( HackRFTunerConfiguration config ) + { + mSelectedConfig = config; + + try + { + mController.apply( config ); + + mName.setText( config.getName() ); + mFrequencyCorrection.setValue( config.getFrequencyCorrection() ); + + mComboLNAGain.setSelectedItem( mSelectedConfig.getLNAGain() ); + mComboVGAGain.setSelectedItem( mSelectedConfig.getVGAGain() ); + + mComboSampleRate.setSelectedItem( mSelectedConfig.getSampleRate() ); + + mResourceManager.getSettingsManager().setSelectedTunerConfiguration( + TunerType.HACKRF, mController.getSerial().getSerialNumber(), config ); + } + catch ( UsbException | SourceException e1 ) + { + JOptionPane.showMessageDialog( + HackRFTunerConfigurationPanel.this, + "HackRF Tuner Controller - couldn't " + + "apply the tuner configuration settings - " + + e1.getLocalizedMessage() ); + + mLog.error( "HackRF 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 getModel() + { + ArrayList configs = + mResourceManager.getSettingsManager() + .getTunerConfigurations( TunerType.HACKRF ); + + DefaultComboBoxModel model = + new DefaultComboBoxModel(); + + for( TunerConfiguration config: configs ) + { + model.addElement( (HackRFTunerConfiguration)config ); + } + + return model; + } + + /** + * Retrieves a specific (named) tuner configuration + */ + private HackRFTunerConfiguration getNamedConfiguration( String name ) + { + ArrayList configs = + mResourceManager.getSettingsManager() + .getTunerConfigurations( TunerType.HACKRF ); + + for( TunerConfiguration config: configs ) + { + if( config.getName().contentEquals( name ) ) + { + return (HackRFTunerConfiguration)config; + } + } + + return null; + } + + /** + * Saves current tuner configuration settings + */ + private void save() + { + mResourceManager.getSettingsManager().save(); + } +} diff --git a/src/source/tuner/hackrf/HackRFTunerController.java b/src/source/tuner/hackrf/HackRFTunerController.java new file mode 100644 index 00000000..38555d3e --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTunerController.java @@ -0,0 +1,1071 @@ +package source.tuner.hackrf; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedTransferQueue; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.usb.UsbClaimException; +import javax.usb.UsbConfiguration; +import javax.usb.UsbControlIrp; +import javax.usb.UsbDevice; +import javax.usb.UsbDisconnectedException; +import javax.usb.UsbEndpoint; +import javax.usb.UsbEndpointDescriptor; +import javax.usb.UsbException; +import javax.usb.UsbInterface; +import javax.usb.UsbInterfacePolicy; +import javax.usb.UsbIrp; +import javax.usb.UsbNotActiveException; +import javax.usb.UsbNotOpenException; +import javax.usb.UsbPipe; +import javax.usb.event.UsbPipeDataEvent; +import javax.usb.event.UsbPipeErrorEvent; +import javax.usb.event.UsbPipeListener; + +import org.apache.commons.io.EndianUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.usb4java.LibUsb; + +import sample.Listener; +import source.SourceException; +import source.tuner.TunerConfiguration; +import source.tuner.TunerController; +import source.tuner.rtl.ByteSampleAdapter; +import source.tuner.usb.USBTunerDevice; + +public class HackRFTunerController extends TunerController +{ + private final static Logger mLog = + LoggerFactory.getLogger( HackRFTunerController.class ); + + public static final byte USB_ENDPOINT = (byte)0x81; + public static final byte USB_INTERFACE = (byte)0x0; + public static final boolean USB_FORCE_CLAIM_INTERFACE = true; + + public static final byte REQUEST_TYPE_IN = + (byte)( LibUsb.ENDPOINT_IN | + LibUsb.REQUEST_TYPE_VENDOR | + LibUsb.RECIPIENT_DEVICE ); + + public static final byte REQUEST_TYPE_OUT = + (byte)( LibUsb.ENDPOINT_OUT | + LibUsb.REQUEST_TYPE_VENDOR | + LibUsb.RECIPIENT_DEVICE ); + + public static final long MIN_FREQUENCY = 10000000l; + public static final long MAX_FREQUENCY = 6000000000l; + public static final long DEFAULT_FREQUENCY = 101100000; + + public final static int USB_IRP_POOL_SIZE = 10; + + private LinkedTransferQueue mReceivedBuffers = + new LinkedTransferQueue(); + + private CopyOnWriteArrayList> mSampleListeners = + new CopyOnWriteArrayList>(); + + private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); + private BufferProcessor mBufferProcessor = new BufferProcessor(); + + private UsbDevice mUSBDevice; + private UsbInterface mUSBInterface; + private UsbPipe mUSBPipe; + + private HackRFSampleRate mSampleRate = HackRFSampleRate.RATE4_464MHZ; + public int mBufferSize = mSampleRate.getBufferSize(); + + public HackRFTunerController( USBTunerDevice tunerDevice ) + throws SourceException + { + super( MIN_FREQUENCY, MAX_FREQUENCY ); + + mUSBDevice = tunerDevice.getDevice(); + } + + public void init() throws SourceException + { + mLog.info( "HackRF Tuner Controller init() starting" ); + + UsbConfiguration config = mUSBDevice.getActiveUsbConfiguration(); + + mUSBInterface = config.getUsbInterface( USB_INTERFACE ); + + try + { + if( claim( mUSBInterface ) ) + { + /* Get the USB endpoint */ + UsbEndpoint endpoint = mUSBInterface.getUsbEndpoint( USB_ENDPOINT ); + + UsbEndpointDescriptor d = endpoint.getUsbEndpointDescriptor(); + System.out.println( "Endpoint max packet size:" + d.wMaxPacketSize() ); + + mUSBPipe = endpoint.getUsbPipe(); + + setMode( Mode.RECEIVE ); + + setFrequency( DEFAULT_FREQUENCY ); + } + else + { + throw new SourceException( "HackRF Tuner Controller - couldn't " + + "claim USB interface" ); + } + } + catch( UsbException e ) + { + throw new SourceException( "HackRF Tuner Controller - couldn't " + + "claim USB interface or get endpoint or pipe", e ); + } + catch ( UsbDisconnectedException e ) + { + throw new SourceException( "HackRF Tuner Controller - usb device " + + "is disconnected", e ); + } + + mLog.info( "HackRF Tuner Controller init() complete" ); + } + + /** + * Claims the USB interface. If another application currently has + * the interface claimed, the USB_FORCE_CLAIM_INTERFACE setting + * will dictate if the interface is forcibly claimed from the other + * application + */ + public static boolean claim( UsbInterface iface ) throws UsbException, + UsbClaimException + { + if( !iface.isClaimed() ) + { + iface.claim( new UsbInterfacePolicy() + { + @Override + public boolean forceClaim( UsbInterface arg0 ) + { + return USB_FORCE_CLAIM_INTERFACE; + } + } ); + + return iface.isClaimed(); + } + else + { + mLog.error( "attempt to claim USB interface failed - in use by " + + "another application" ); + } + + return false; + } + + /** + * HackRF board identifier/type + */ + public BoardID getBoardID() throws UsbException + { + int id = readByte( Request.BOARD_ID_READ, (byte)0, (byte)0, false ); + + return BoardID.lookup( id ); + } + + /** + * HackRF firmware version string + */ + public String getFirmwareVersion() throws UsbException + { + byte[] data = readArray( Request.VERSION_STRING_READ, 0, 0, 255 ); + + StringBuilder sb = new StringBuilder(); + + for( byte b: data ) + { + if( b != (byte)0 ) + { + sb.append( (char)b ); + } + } + + return sb.toString(); + } + + /** + * HackRF part id number and serial number + */ + public Serial getSerial() throws UsbException + { + byte[] data = readArray( Request.BOARD_PARTID_SERIALNO_READ, 0, 0, 24 ); + + return new Serial( data ); + } + + /** + * Sets the HackRF transceiver mode + */ + public void setMode( Mode mode ) throws UsbException + { + write( Request.SET_TRANSCEIVER_MODE, mode.getNumber(), 0 ); + } + + /** + * Sets the HackRF baseband filter + */ + public void setBasebandFilter( BasebandFilter filter ) throws UsbException + { + write( Request.BASEBAND_FILTER_BANDWIDTH_SET, + filter.getLowValue(), + filter.getHighValue() ); + } + + /** + * Enables (true) or disables (false) the amplifier + */ + public void setAmplifierEnabled( boolean enabled ) throws UsbException + { + write( Request.AMP_ENABLE, ( enabled ? 1 : 0 ), 0 ); + } + + /** + * Sets the IF LNA Gain + */ + public void setLNAGain( HackRFLNAGain gain ) throws UsbException + { + int result = readByte( Request.SET_LNA_GAIN, 0, gain.getValue(), true ); + + if( result != 1 ) + { + throw new UsbException( "couldn't set lna gain to " + gain ); + } + } + + /** + * Sets the Baseband VGA Gain + */ + public void setVGAGain( HackRFVGAGain gain ) throws UsbException + { + int result = readByte( Request.SET_VGA_GAIN, 0, gain.getValue(), true ); + + if( result != 1 ) + { + throw new UsbException( "couldn't set vga gain to " + gain ); + } + } + + /** + * Not implemented + */ + public long getTunedFrequency() throws SourceException + { + return 0; + } + + @Override + public void setTunedFrequency( long frequency ) throws SourceException + { + int mhz = (int)( frequency / 1E6 ); + int hz = (int)( frequency - ( mhz * 1E6 ) ); + + byte[] data = new byte[ 8 ]; + + EndianUtils.writeSwappedInteger( data, 0, mhz ); + EndianUtils.writeSwappedInteger( data, 4, hz ); + + try + { + write( Request.SET_FREQUENCY, 0, 0, data ); + } + catch( UsbException e ) + { + mLog.error( "error setting frequency [" + frequency + "]", e ); + + throw new SourceException( "error setting frequency [" + + frequency + "]", e ); + } + + } + + @Override + public int getCurrentSampleRate() throws SourceException + { + return mSampleRate.getRate(); + } + + @Override + public void apply( TunerConfiguration config ) throws SourceException + { + if( config instanceof HackRFTunerConfiguration ) + { + HackRFTunerConfiguration hackConfig = + (HackRFTunerConfiguration)config; + + try + { + setSampleRate( hackConfig.getSampleRate() ); + setFrequencyCorrection( hackConfig.getFrequencyCorrection() ); + setLNAGain( hackConfig.getLNAGain() ); + setVGAGain( hackConfig.getVGAGain() ); + setFrequency( getFrequency() ); + } + catch ( UsbException e ) + { + throw new SourceException( "Error while applying tuner " + + "configuration", e ); + } + } + else + { + throw new IllegalArgumentException( "Invalid tuner configuration " + + "type [" + config.getClass() + "]" ); + } + } + + public byte[] readArray( Request request, + int value, + int index, + int length ) throws UsbException + { + UsbControlIrp irp = mUSBDevice.createUsbControlIrp( REQUEST_TYPE_IN, + request.getRequestNumber(), (short)value, (short)index ); + + byte[] data = new byte[ length ]; + + irp.setData( data ); + + mUSBDevice.syncSubmit( irp ); + + return data; + } + + public int read( Request request, int value, int index, int length ) + throws UsbException + { + if( !( length == 1 || length == 2 || length == 4 ) ) + { + throw new IllegalArgumentException( "invalid length [" + length + + "] must be: byte=1, short=2, int=3 to read a primitive" ); + } + + byte[] data = readArray( request, value, index, length ); + + switch( data.length ) + { + case 1: + return data[ 0 ]; + case 2: + return EndianUtils.readSwappedShort( data, 0 ); + case 4: + return EndianUtils.readSwappedInteger( data, 0 ); + default: + throw new UsbException( "read() primitive returned an " + + "unrecognized byte array " + Arrays.toString( data ) ); + } + } + + public int readByte( Request request, int value, int index, boolean signed ) + throws UsbException + { + byte[] data = readArray( request, value, index, 1 ); + + if( signed ) + { + return (int)( data[ 0 ] ); + } + else + { + return (int)( data[ 0 ] & 0xFF ); + } + } + + public void write( Request request, + int value, + int index, + byte[] data ) throws UsbException + { + UsbControlIrp irp = mUSBDevice.createUsbControlIrp( REQUEST_TYPE_OUT, + request.getRequestNumber(), (short)value, (short)index ); + + irp.setData( data ); + + mUSBDevice.syncSubmit( irp ); + } + + /** + * Sends a request that doesn't have a data payload + */ + public void write( Request request, + int value, + int index ) throws UsbException + { + write( request, value, index, new byte[ 0 ] ); + } + + /** + * Sample Rate + * + * Note: this is direct translation of the libhackrf code, because I still + * can't figure out how it works ... one day, maybe. + */ + public void setSampleRate( HackRFSampleRate rate ) throws UsbException + { +// int MAX_N = 32; +// +// double freq = (double)rate.getRate() / 1E6d; +// +// double freq_frac = 1.0d + freq - (int)freq; +// +// long u64 = Double.doubleToLongBits( freq ); +// +// int e = (int)( ( u64 >> 52 ) - 1023 ); +// +// long m = (long)( 1l << 52 ) - 1l; +// +// u64 = Double.doubleToLongBits( freq_frac ); +// +// u64 &= m; +// +// m &= ~( ( 1 << ( e + 4 ) ) - 1 ); +// +// long a = 0; +// +// int x; +// +// for( x = 1; x < MAX_N; x++ ) +// { +// a += u64; +// +// if( ( ( a & m ) == 0 ) || ( ( ~a & m ) == 0 ) ) +// { +// break; +// } +// } +// +// if( x == MAX_N ) +// { +// x = 1; +// } +// +// int freq_hz = (int)( freq * (double)x * 0.5d ); +// +// setSampleRateManual( freq_hz, x ); + + setSampleRateManual( rate.getRate(), 1 ); + + mFrequencyController.setSampleRate( rate.getRate() ); + + mBufferSize = rate.getBufferSize(); + + setBasebandFilter( rate.getFilter() ); + + mSampleRate = rate; + } + + public void setSampleRateManual( int frequency, int divider ) + throws UsbException + { + byte[] data = new byte[ 8 ]; + + EndianUtils.writeSwappedInteger( data, 0, frequency ); + EndianUtils.writeSwappedInteger( data, 4, divider ); + + write( Request.SET_SAMPLE_RATE, 0, 0, data ); + } + + public int getSampleRate() + { + return mSampleRate.getRate(); + } + + public enum Request + { + SET_TRANSCEIVER_MODE( 1 ), + MAX2837_TRANSCEIVER_WRITE( 2 ), + MAX2837_TRANSCEIVER_READ( 3 ), + SI5351C_CLOCK_GENERATOR_WRITE( 4 ), + SI5351C_CLOCK_GENERATOR_READ( 5 ), + SET_SAMPLE_RATE( 6 ), + BASEBAND_FILTER_BANDWIDTH_SET( 7 ), + RFFC5071_MIXER_WRITE( 8 ), + RFFC5071_MIXER_READ( 9 ), + SPIFLASH_ERASE( 10 ), + SPIFLASH_WRITE( 11 ), + SPIFLASH_READ( 12 ), + BOARD_ID_READ( 14 ), + VERSION_STRING_READ( 15 ), + SET_FREQUENCY( 16 ), + AMP_ENABLE( 17 ), + BOARD_PARTID_SERIALNO_READ( 18 ), + SET_LNA_GAIN( 19 ), + SET_VGA_GAIN( 20 ), + SET_TXVGA_GAIN( 21 ), + ANTENNA_ENABLE( 23 ), + SET_FREQUENCY_EXPLICIT( 24 ); + + private byte mRequestNumber; + + private Request( int number ) + { + mRequestNumber = (byte)number; + } + + public byte getRequestNumber() + { + return mRequestNumber; + } + } + + public enum HackRFSampleRate + { + RATE2_016MHZ( 2016000, 65536, "2.016 MHz", BasebandFilter.F2_50 ), + RATE3_024MHZ( 3024000, 98304, "3.024 MHz", BasebandFilter.F3_50 ), + RATE4_464MHZ( 4464000, 131072, "4.464 MHz", BasebandFilter.F5_00 ), + RATE5_472MHZ( 5472000, 196608, "5.472 MHz", BasebandFilter.F6_00 ), + RATE7_488MHZ( 7488000, 262144, "7.488 MHz", BasebandFilter.F8_00 ), + RATE10_032MHZ( 9984000, 131072, " 9.984 MHz", BasebandFilter.F10_00 ), + RATE12_000MHZ( 12000000, 262144, "12.000 MHz", BasebandFilter.F12_00 ), + RATE13_488MHZ( 13488000, 262144, "13.488 MHz", BasebandFilter.F14_00 ), + RATE14_976MHZ( 14976000, 262144, "14.976 MHz", BasebandFilter.F15_00 ), + RATE19_968MHZ( 19968000, 262144, "19.968 MHz", BasebandFilter.F20_00 ); + + private int mRate; + private int mBufferSize; + private String mLabel; + private BasebandFilter mFilter; + + private HackRFSampleRate( int rate, + int bufferSize, + String label, + BasebandFilter filter ) + { + mRate = rate; + mBufferSize = bufferSize; + mLabel = label; + mFilter = filter; + } + + public int getRate() + { + return mRate; + } + + public int getBufferSize() + { + return mBufferSize; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return mLabel; + } + + public BasebandFilter getFilter() + { + return mFilter; + } + } + + public enum BasebandFilter + { + F1_75( 1750000, "1.75 MHz" ), + F2_50( 2500000, "2.50 MHz" ), + F3_50( 3500000, "3.50 MHz" ), + F5_00( 5000000, "5.00 MHz" ), + F5_50( 5500000, "5.50 MHz" ), + F6_00( 6000000, "6.00 MHz" ), + F7_00( 7000000, "7.00 MHz" ), + F8_00( 8000000, "8.00 MHz" ), + F9_00( 9000000, "9.00 MHz" ), + F10_00( 10000000, "10.00 MHz" ), + F12_00( 12000000, "12.00 MHz" ), + F14_00( 14000000, "14.00 MHz" ), + F15_00( 15000000, "15.00 MHz" ), + F20_00( 20000000, "20.00 MHz" ), + F24_00( 24000000, "24.00 MHz" ), + F28_00( 28000000, "28.00 MHz" ); + + private int mBandwidth; + private String mLabel; + + private BasebandFilter( int bandwidth, String label ) + { + mBandwidth = bandwidth; + mLabel = label; + } + + public int getBandwidth() + { + return mBandwidth; + } + + public int getHighValue() + { + return mBandwidth >> 16; + } + + public int getLowValue() + { + return mBandwidth & 0xFFFF; + } + + public String getLabel() + { + return mLabel; + } + } + + public enum BoardID + { + JELLYBEAN( 0x00, "HackRF Jelly Bean" ), + JAWBREAKER( 0x01, "HackRF Jaw Breaker" ), + HACKRF_ONE( 0x02, "HackRF One" ), + INVALID( 0xFF, "HackRF Unknown Board" ); + + private byte mIDNumber; + private String mLabel; + + private BoardID( int number, String label ) + { + mIDNumber = (byte)number; + mLabel = label; + } + + public String toString() + { + return mLabel; + } + + public String getLabel() + { + return mLabel; + } + + public byte getNumber() + { + return mIDNumber; + } + + public static BoardID lookup( int value ) + { + switch( value ) + { + case 0: + return JELLYBEAN; + case 1: + return JAWBREAKER; + case 2: + return HACKRF_ONE; + default: + return INVALID; + } + } + } + + public enum Mode + { + OFF( 0, "Off" ), + RECEIVE( 1, "Receive" ), + TRANSMIT( 2, "Transmit" ), + SS( 3, "SS" ); + + private byte mNumber; + private String mLabel; + + private Mode( int number, String label ) + { + mNumber = (byte)number; + mLabel = label; + } + + public byte getNumber() + { + return mNumber; + } + + public String getLabel() + { + return mLabel; + } + + public String toString() + { + return mLabel; + } + } + + public enum HackRFLNAGain + { + GAIN_0( 0 ), + GAIN_8( 8 ), + GAIN_16( 16 ), + GAIN_24( 24 ), + GAIN_32( 32 ), + GAIN_40( 40 ); + + private int mValue; + + private HackRFLNAGain( int value ) + { + mValue = value; + } + + public int getValue() + { + return mValue; + } + + public String toString() + { + return String.valueOf( mValue ) + " dB"; + } + } + + /** + * Receive (baseband) VGA Gain values + */ + public enum HackRFVGAGain + { + GAIN_0( 0 ), + GAIN_2( 2 ), + GAIN_4( 4 ), + GAIN_6( 6 ), + GAIN_8( 8 ), + GAIN_10( 10 ), + GAIN_12( 12 ), + GAIN_14( 14 ), + GAIN_16( 16 ), + GAIN_18( 18 ), + GAIN_20( 20 ), + GAIN_22( 22 ), + GAIN_23( 24 ), + GAIN_26( 26 ), + GAIN_28( 28 ), + GAIN_30( 30 ), + GAIN_32( 32 ), + GAIN_34( 34 ), + GAIN_36( 36 ), + GAIN_38( 38 ), + GAIN_40( 40 ), + GAIN_42( 42 ), + GAIN_44( 44 ), + GAIN_46( 46 ), + GAIN_48( 48 ), + GAIN_50( 50 ), + GAIN_52( 52 ), + GAIN_54( 54 ), + GAIN_56( 56 ), + GAIN_58( 58 ), + GAIN_60( 60 ), + GAIN_62( 62 ); + + private int mValue; + + private HackRFVGAGain( int value ) + { + mValue = value; + } + + public int getValue() + { + return mValue; + } + + public String toString() + { + return String.valueOf( mValue ) + " dB"; + } + } + + /** + * HackRF part id and serial number parsing class + */ + public class Serial + { + private byte[] mData; + + public Serial( byte[] data ) + { + mData = data; + } + + public String getPartID() + { + int part0 = EndianUtils.readSwappedInteger( mData, 0 ); + int part1 = EndianUtils.readSwappedInteger( mData, 4 ); + + StringBuilder sb = new StringBuilder(); + + sb.append( String.format( "%08X", part0 ) ); + sb.append( "-" ); + sb.append( String.format( "%08X", part1 ) ); + + return sb.toString(); + } + + public String getSerialNumber() + { + int serial0 = EndianUtils.readSwappedInteger( mData, 8 ); + int serial1 = EndianUtils.readSwappedInteger( mData, 12 ); + int serial2 = EndianUtils.readSwappedInteger( mData, 16 ); + int serial3 = EndianUtils.readSwappedInteger( mData, 20 ); + + StringBuilder sb = new StringBuilder(); + + sb.append( String.format( "%08X", serial0 ) ); + sb.append( "-" ); + sb.append( String.format( "%08X", serial1 ) ); + sb.append( "-" ); + sb.append( String.format( "%08X", serial2 ) ); + sb.append( "-" ); + sb.append( String.format( "%08X", serial3 ) ); + + return sb.toString(); + } + } + + /** + * Adds a sample listener. If the buffer processing thread is + * not currently running, starts it running in a new thread. + */ + public void addListener( Listener listener ) + { + mSampleListeners.add( listener ); + + System.out.println( "HackRF - listener added - count:" + mSampleListeners.size() ); + mBufferProcessor.start(); + } + + /** + * Removes the sample listener. If this is the last registered listener, + * shuts down the buffer processing thread. + */ + public void removeListener( Listener listener ) + { + mSampleListeners.remove( listener ); + + if( mSampleListeners.isEmpty() ) + { + mBufferProcessor.stop(); + } + } + + /** + * Dispatches the sample array to each registered listener. If there is more + * than one listener, they receive a copy of the samples. If there is only + * one listener, or the last listener, they receive the original sample array. + * + * This is to facilitate garbage collection of the array when the listener + * is done processing the samples. + */ + public void broadcast( Float[] samples ) + { + Iterator> it = mSampleListeners.iterator(); + + while( it.hasNext() ) + { + Listener next = it.next(); + + /* if this is the last (or only) listener, send the original + * buffer, otherwise send a copy of the buffer */ + if( it.hasNext() ) + { + next.receive( Arrays.copyOf( samples, samples.length ) ); + } + else + { + next.receive( samples ); + } + } + } + + /** + * Buffer processing thread. Fetches samples from the RTL2832 Tuner and + * dispatches them to all registered listeners + */ + public class BufferProcessor implements UsbPipeListener + { + private ScheduledExecutorService mExecutor = + Executors.newScheduledThreadPool( 2 ); + private ScheduledFuture mSampleDispatcherTask; +// private ScheduledFuture mSampleRateCounterTask; + + private AtomicBoolean mRunning = new AtomicBoolean(); + private AtomicBoolean mResetting = new AtomicBoolean(); + private Boolean mTransitioning = false; + + public void start() + { + synchronized( mTransitioning ) + { + if( mRunning.compareAndSet( false, true ) ) + { + mLog.debug( "HackRF - starting sample fetch thread" ); + + ArrayList irps = new ArrayList(); + + for( int x = 0; x < USB_IRP_POOL_SIZE; x++ ) + { + UsbIrp irp = mUSBPipe.createUsbIrp(); + irp.setData( new byte[ mBufferSize ] ); + irps.add( irp ); + } + + try + { + if( mUSBPipe.isActive() ) + { + if( !mUSBPipe.isOpen() ) + { + mUSBPipe.open(); + + mUSBPipe.addUsbPipeListener( this ); + } + + if( mUSBPipe.isOpen() ) + { + mSampleDispatcherTask = mExecutor + .scheduleAtFixedRate( new BufferDispatcher(), + 0, 20, TimeUnit.MILLISECONDS ); + +// mSampleRateMonitor = +// new SampleRateMonitor( mSampleRate.getRate() ); + +// mSampleRateCounterTask = mExecutor +// .scheduleAtFixedRate( mSampleRateMonitor, +// 10, 10, TimeUnit.SECONDS ); + + mUSBPipe.asyncSubmit( irps ); + } + } + } + catch ( Exception e ) + { + mLog.error( "Error in buffer processor thread", e ); + } + } + } + } + + /** + * Stops the sample fetching thread + */ + public void stop() + { + synchronized( mTransitioning ) + { + if( mRunning.compareAndSet( true, false ) ) + { + mLog.debug( "HackRF - stopping sample fetch thread" ); + + mUSBPipe.abortAllSubmissions(); + + if( mSampleDispatcherTask != null ) + { + mSampleDispatcherTask.cancel( true ); +// mSampleRateCounterTask.cancel( true ); +// mRawSampleBuffer.clear(); +// mSampleCounter.set( 0 ); + } + } + } + } + + /** + * Indicates if this thread is running + */ + public boolean isRunning() + { + return mRunning.get(); + } + + /** + * Receives filled data buffers and places them in the queue for the + * BufferDispatcher to process + */ + @Override + public synchronized void dataEventOccurred( UsbPipeDataEvent event ) + { + mReceivedBuffers.add( event.getUsbIrp() ); + } + + @Override + public void errorEventOccurred( UsbPipeErrorEvent e ) + { + /* When an error occurs, we'll get errors on all of the + * queued IRPS in succession. We use the mResetting to control + * the reset process and ignore all subsequent errors */ + if( mResetting.compareAndSet( false, true ) ) + { + mLog.error( "error during data transfer resetting USB pipe to " + + "HackRF", e ); + + stop(); + + if( !mSampleListeners.isEmpty() ) + { + start(); + } + + mResetting.set( false ); + } + } + } + + /** + * Fetches received UsbIrp buffers from the received queue, dispatches them + * and resubmits the processed buffer for more samples + */ + public class BufferDispatcher implements Runnable + { + @Override + public void run() + { + ArrayList buffers = new ArrayList(); + + mReceivedBuffers.drainTo( buffers ); + + for( UsbIrp buffer: buffers ) + { + Float[] samples = mSampleAdapter.convert( buffer.getData() ); + + broadcast( samples ); + + if( mBufferProcessor != null && mBufferProcessor.isRunning() ) + { + try + { + /* We adjust the buffer size according to sample rate. + * If this byte array matches the current buffer size + * then we reuse the byte array, otherwise create a + * new one. */ + if( buffer.getData().length == mBufferSize ) + { + mUSBPipe.asyncSubmit( buffer.getData() ); + } + else + { + mUSBPipe.asyncSubmit( new byte[ mBufferSize ] ); + } + } + catch ( UsbNotActiveException | UsbNotOpenException + | IllegalArgumentException | UsbDisconnectedException + | UsbException e ) + { + mLog.error( "error submitting a byte array to the usb " + + "pipe for samples", e ); + } + } + } + } + } +} diff --git a/src/source/tuner/hackrf/HackRFTunerEditorPanel.java b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java new file mode 100644 index 00000000..5f4dec78 --- /dev/null +++ b/src/source/tuner/hackrf/HackRFTunerEditorPanel.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * 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 + ******************************************************************************/ +package source.tuner.hackrf; + +import gui.control.JFrequencyControl; + +import java.awt.Color; + +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.usb.UsbException; + +import net.miginfocom.swing.MigLayout; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import source.SourceException; +import source.tuner.FrequencyChangeEvent; +import source.tuner.FrequencyChangeEvent.Attribute; +import source.tuner.FrequencyChangeListener; +import source.tuner.hackrf.HackRFTunerController.BoardID; + +import com.jidesoft.swing.JideTabbedPane; + +import controller.ResourceManager; + +public class HackRFTunerEditorPanel extends JPanel + implements FrequencyChangeListener +{ + private static final long serialVersionUID = 1L; + + private final static Logger mLog = + LoggerFactory.getLogger( HackRFTunerEditorPanel.class ); + + private JFrequencyControl mFrequencyControl; + + private HackRFTuner mTuner; + private HackRFTunerController mController; + private ResourceManager mResourceManager; + + public HackRFTunerEditorPanel( HackRFTuner tuner, + ResourceManager resourceManager ) + { + mTuner = tuner; + mController = mTuner.getController(); + mResourceManager = resourceManager; + + initGUI(); + } + + private void initGUI() + { + setLayout( new MigLayout( "fill,wrap 2", + "[right,grow][grow]", + "[][][][][][grow]" ) ); + + BoardID board = BoardID.INVALID; + + try + { + board = mTuner.getController().getBoardID(); + } + catch ( UsbException e ) + { + mLog.error( "couldn't read HackRF board identifier", e ); + } + + add( new JLabel( board.getLabel() ), "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.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 HackRFTunerConfigurationPanel( + mResourceManager, mTuner.getController() ) ); + + tabs.add( "Info", new HackRFInformationPanel( mTuner.getController() ) ); + } + + @Override + public void frequencyChanged( FrequencyChangeEvent event ) + { + if( event.getAttribute() == Attribute.FREQUENCY ) + { + try + { + mController.setFrequency( event.getValue() ); + } + catch ( SourceException e ) + { + mLog.error( "error setting frequency [" + event.getValue() + "]", e ); + } + } + } +} diff --git a/src/source/tuner/rtl/RTL2832SampleAdapter.java b/src/source/tuner/rtl/ByteSampleAdapter.java similarity index 82% rename from src/source/tuner/rtl/RTL2832SampleAdapter.java rename to src/source/tuner/rtl/ByteSampleAdapter.java index d11fdfd6..cecfe182 100644 --- a/src/source/tuner/rtl/RTL2832SampleAdapter.java +++ b/src/source/tuner/rtl/ByteSampleAdapter.java @@ -19,9 +19,9 @@ package source.tuner.rtl; import source.mixer.SampleAdapter; -public class RTL2832SampleAdapter extends SampleAdapter +public class ByteSampleAdapter extends SampleAdapter { - private final static float[] mLOOKUP_VALUES; + private final static float[] LOOKUP_VALUES; /** * Creates a static lookup table that converts the 8-bit valued range @@ -29,14 +29,14 @@ public class RTL2832SampleAdapter extends SampleAdapter */ static { - mLOOKUP_VALUES = new float[ 256 ]; + LOOKUP_VALUES = new float[ 256 ]; for( int x = 0; x < 256; x++ ) { - mLOOKUP_VALUES[ x ] = (float)( x - 128 ) * 256.0f; + LOOKUP_VALUES[ x ] = (float)( x - 128 ) * 256.0f; } - mLOOKUP_VALUES[ 128 ] = 0.0000001f; + LOOKUP_VALUES[ 128 ] = 0.0000001f; } @Override @@ -48,7 +48,7 @@ public class RTL2832SampleAdapter extends SampleAdapter for( byte sample: samples ) { /* Convert byte value into float from the lookup table */ - convertedSamples[ pointer++ ] = mLOOKUP_VALUES[ ( sample & 0xFF ) ]; + convertedSamples[ pointer++ ] = LOOKUP_VALUES[ ( sample & 0xFF ) ]; } return convertedSamples; diff --git a/src/source/tuner/rtl/RTL2832TunerController.java b/src/source/tuner/rtl/RTL2832TunerController.java index 5d090bb4..0ea89eb1 100644 --- a/src/source/tuner/rtl/RTL2832TunerController.java +++ b/src/source/tuner/rtl/RTL2832TunerController.java @@ -155,7 +155,7 @@ public abstract class RTL2832TunerController extends TunerController private BufferProcessor mBufferProcessor = new BufferProcessor(); - private RTL2832SampleAdapter mSampleAdapter = new RTL2832SampleAdapter(); + private ByteSampleAdapter mSampleAdapter = new ByteSampleAdapter(); private CopyOnWriteArrayList> mSampleListeners = new CopyOnWriteArrayList>(); diff --git a/src/spectrum/ChannelSpectrumPanel.java b/src/spectrum/ChannelSpectrumPanel.java index 90229687..628e18cc 100644 --- a/src/spectrum/ChannelSpectrumPanel.java +++ b/src/spectrum/ChannelSpectrumPanel.java @@ -77,8 +77,9 @@ public class ChannelSpectrumPanel extends JPanel mOverlayPanel = new ChannelOverlayPanel( mResourceManager ); - mSpectrumAveragingSize = mResourceManager.getSettingsManager() - .getIntegerSetting( CHANNEL_SPECTRUM_AVERAGING_SIZE, 10 ).getValue(); + mSpectrumAveragingSize = 10; +// mSpectrumAveragingSize = mResourceManager.getSettingsManager() +// .getIntegerSetting( CHANNEL_SPECTRUM_AVERAGING_SIZE, 10 ).getValue(); mSpectrumPanel.setAveraging( mSpectrumAveragingSize ); diff --git a/src/spectrum/SpectrumPanel.java b/src/spectrum/SpectrumPanel.java index f757db15..f34646a1 100644 --- a/src/spectrum/SpectrumPanel.java +++ b/src/spectrum/SpectrumPanel.java @@ -34,7 +34,6 @@ import org.slf4j.LoggerFactory; import settings.ColorSetting; import settings.ColorSetting.ColorSettingName; -import settings.IntegerSetting; import settings.Setting; import settings.SettingChangeListener; import buffer.FloatArrayCircularAveragingBuffer; @@ -97,8 +96,8 @@ public class SpectrumPanel extends JPanel { mAveraging = size; - mResourceManager.getSettingsManager().setIntegerSetting( - SPECTRUM_AVERAGING_SIZE, mAveraging ); +// mResourceManager.getSettingsManager().setIntegerSetting( +// SPECTRUM_AVERAGING_SIZE, mAveraging ); mFFTAveragingBuffer = new FloatArrayCircularAveragingBuffer( mAveraging ); @@ -149,16 +148,17 @@ public class SpectrumPanel extends JPanel mColorSpectrumLine = getColor( ColorSettingName.SPECTRUM_LINE ); - mAveraging = getIntegerSetting( "spectrum_averaging_size", 4 ); + mAveraging = 4; +// mAveraging = getIntegerSetting( "spectrum_averaging_size", 4 ); } - private int getIntegerSetting( String name, int defaultValue ) - { - IntegerSetting setting = mResourceManager.getSettingsManager() - .getIntegerSetting( name, defaultValue ); - - return setting.getValue(); - } +// private int getIntegerSetting( String name, int defaultValue ) +// { +// IntegerSetting setting = mResourceManager.getSettingsManager() +// .getIntegerSetting( name, defaultValue ); +// +// return setting.getValue(); +// } private Color getColor( ColorSettingName name ) { diff --git a/src/util/Oscillator.java b/src/util/Oscillator.java index 302beda4..f6dba831 100644 --- a/src/util/Oscillator.java +++ b/src/util/Oscillator.java @@ -69,7 +69,6 @@ public class Oscillator */ public void setSampleRate( int sampleRate ) { - System.out.println( "Oscillator - setting sampleRate to:" + sampleRate ); mSampleRate = (double)sampleRate; update(); }